home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-05-03 | 90.2 KB | 4,340 lines |
- Newsgroups: comp.sources.unix
- From: pmiller@bmr.gov.au (Peter Miller)
- Subject: v26i214: cook-1.4 - a file construction tool (like "make"), Part06/11
- Sender: unix-sources-moderator@efficacy.home.vix.com
- Approved: WhoAmI@efficacy.home.vix.com
-
- Submitted-By: pmiller@bmr.gov.au (Peter Miller)
- Posting-Number: Volume 26, Issue 214
- Archive-Name: cook-1.4/part06
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of archive 6 (of 11)."
- # Contents: c_incl/cache.c c_incl/main.c c_incl/sniff.c
- # common/arglex.c config cook/expr.c cook/hashline.y
- # Wrapped by vixie@efficacy.home.vix.com on Tue May 4 01:36:40 1993
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'c_incl/cache.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'c_incl/cache.c'\"
- else
- echo shar: Extracting \"'c_incl/cache.c'\" \(12450 characters\)
- sed "s/^X//" >'c_incl/cache.c' <<'END_OF_FILE'
- X/*
- X * cook - file construction tool
- X * Copyright (C) 1991, 1992, 1993 Peter Miller.
- X * All rights reserved.
- X *
- X * This program is free software; you can redistribute it and/or modify
- X * it under the terms of the GNU General Public License as published by
- X * the Free Software Foundation; either version 2 of the License, or
- X * (at your option) any later version.
- X *
- X * This program is distributed in the hope that it will be useful,
- X * but WITHOUT ANY WARRANTY; without even the implied warranty of
- X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- X * GNU General Public License for more details.
- X *
- X * You should have received a copy of the GNU General Public License
- X * along with this program; if not, write to the Free Software
- X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- X *
- X * MANIFEST: functions to manipulate include file cache
- X */
- X
- X#include <stdio.h>
- X
- X#include <arglex.h>
- X#include <cache.h>
- X#include <error.h>
- X#include <mem.h>
- X#include <os.h>
- X
- Xtypedef struct node node;
- Xstruct node
- X{
- X cache_ty cache;
- X node *next;
- X};
- X
- X
- Xstatic node **hash_table;
- Xstatic str_hash_ty hash_modulus;
- Xstatic str_hash_ty hash_cutover;
- Xstatic str_hash_ty hash_cutover_mask;
- Xstatic str_hash_ty hash_cutover_split_mask;
- Xstatic str_hash_ty hash_split;
- Xstatic str_hash_ty hash_load;
- Xstatic int need_to_write;
- X
- X
- X/*
- X * NAME
- X * cache_initialize - start up cache
- X *
- X * SYNOPSIS
- X * void cache_initialize(void);
- X *
- X * DESCRIPTION
- X * The cache_initialize function is used to create the hash table.
- X *
- X * RETURNS
- X * void
- X *
- X * CAVEAT
- X * Assumes the str_initialize function has been called already.
- X */
- X
- Xvoid
- Xcache_initialize()
- X{
- X str_hash_ty j;
- X
- X assert(!hash_modulus);
- X hash_modulus = 1<<8; /* MUST be a power of 2 */
- X hash_cutover = hash_modulus;
- X hash_split = hash_modulus - hash_cutover;
- X hash_cutover_mask = hash_cutover - 1;
- X hash_cutover_split_mask = (hash_cutover * 2) - 1;
- X hash_load = 0;
- X hash_table = (node **)mem_alloc(hash_modulus * sizeof(node*));
- X for (j = 0; j < hash_modulus; ++j)
- X hash_table[j] = 0;
- X}
- X
- X
- X/*
- X * NAME
- X * split - reduce symbol table load
- X *
- X * SYNOPSIS
- X * void split(void);
- X *
- X * DESCRIPTION
- X * The split function is used to split symbols in the bucket indicated by
- X * the split point. The symbols are split between that bucket and the one
- X * after the current end of the table.
- X *
- X * RETURNS
- X * void
- X *
- X * CAVEAT
- X * It is only sensable to do this when the symbol table load exceeds some
- X * reasonable threshold. A threshold of 80% is suggested.
- X */
- X
- Xstatic void split _((void));
- X
- Xstatic void
- Xsplit()
- X{
- X node *p;
- X node **ipp;
- X node *p2;
- X str_hash_ty index;
- X
- X /*
- X * get the list to be split across buckets
- X */
- X p = hash_table[hash_split];
- X hash_table[hash_split] = 0;
- X
- X /*
- X * increase the modulus by one
- X */
- X hash_modulus++;
- X mem_change_size((char **)&hash_table, hash_modulus * sizeof(node*));
- X hash_table[hash_modulus - 1] = 0;
- X hash_split = hash_modulus - hash_cutover;
- X if (hash_split >= hash_cutover)
- X {
- X hash_cutover = hash_modulus;
- X hash_split = 0;
- X hash_cutover_mask = hash_cutover - 1;
- X hash_cutover_split_mask = (hash_cutover * 2) - 1;
- X }
- X
- X /*
- X * now redistribute the list elements
- X *
- X * It is important to preserve the order of the links because they can be
- X * push-down stacks, and to simply add them to the head of the list will
- X * reverse the order of the stack!
- X */
- X while (p)
- X {
- X p2 = p;
- X p = p2->next;
- X p2->next = 0;
- X
- X index = p2->cache.filename->str_hash & hash_cutover_mask;
- X if (index < hash_split)
- X index = p2->cache.filename->str_hash & hash_cutover_split_mask;
- X for (ipp = &hash_table[index]; *ipp; ipp = &(*ipp)->next)
- X ;
- X *ipp = p2;
- X }
- X}
- X
- X
- X/*
- X * NAME
- X * cache_search - search for a variable
- X *
- X * SYNOPSIS
- X * int cache_search(string_ty *filename);
- X *
- X * DESCRIPTION
- X * The cache_search function is used to search for
- X * a filename in the cache.
- X *
- X * RETURNS
- X * If the variable has been defined, the function returns a non-zero value
- X * and the value is returned through the 'value' pointer.
- X * If the variable has not been defined, it returns zero,
- X * and 'value' is unaltered.
- X *
- X * CAVEAT
- X * The value returned from this function, when returned, is allocated
- X * in dynamic memory (it is a copy of the value remembered by this module).
- X * It is the responsibility of the caller to free it when finished with,
- X * by a wl_free() call.
- X */
- X
- Xcache_ty *
- Xcache_search(filename)
- X string_ty *filename;
- X{
- X str_hash_ty index;
- X node *p;
- X
- X /*
- X * work out what index in must have
- X */
- X assert(hash_modulus);
- X index = filename->str_hash & hash_cutover_mask;
- X if (index < hash_split)
- X index = filename->str_hash & hash_cutover_split_mask;
- X
- X /*
- X * search down that hash chain
- X */
- X for (p = hash_table[index]; p; p = p->next)
- X {
- X if (str_equal(filename, p->cache.filename))
- X return &p->cache;
- X }
- X
- X /*
- X * not there, so make one
- X */
- X p = (node *)mem_alloc_clear(sizeof(node));
- X p->next = hash_table[index];
- X hash_table[index] = p;
- X p->cache.filename = str_copy(filename);
- X
- X /*
- X * split if the load gets too high
- X */
- X hash_load++;
- X while (hash_load * 10 >= hash_modulus * 8)
- X split();
- X
- X /*
- X * return new cache entry
- X */
- X return &p->cache;
- X}
- X
- X
- X/*
- X * NAME
- X * build_filename - for cache file
- X *
- X * SYNOPSIS
- X * void build_filename(char *buffer);
- X *
- X * DESCRIPTION
- X * The build_filename function is used to build
- X * the name of the cache file.
- X *
- X * ARGUMENTS
- X * buffer - where to put the file name
- X *
- X * CAVEATS
- X * The cache file is in the current directory.
- X */
- X
- Xstatic void build_filename _((char *));
- X
- Xstatic void
- Xbuild_filename(buffer)
- X char *buffer;
- X{
- X sprintf(buffer, ".%src", progname);
- X}
- X
- X
- X/*
- X * NAME
- X * fread_sane - a saner version of fread
- X *
- X * SYNOPSIS
- X * int fread_sane(FILE *fp, void *buf, size_t buflen);
- X *
- X * DESCRIPTION
- X * The fread_sane function is used to read from a standard stream.
- X *
- X * ARGUMENTS
- X * fp - the stream to read from
- X * buf - where to place the bytes read
- X * buflen - number of bytes to read
- X *
- X * RETURNS
- X * 0 on no error, -1 on any error
- X *
- X * CAVEATS
- X * This version considers it to be an error if end-of-file is reached.
- X */
- X
- Xstatic int fread_sane _((FILE *, void *, size_t));
- X
- Xstatic int
- Xfread_sane(fp, buf, buflen)
- X FILE *fp;
- X void *buf;
- X size_t buflen;
- X{
- X if (fread(buf, 1, buflen, fp) != buflen)
- X return -1;
- X return 0;
- X}
- X
- X
- X/*
- X * NAME
- X * cache_read_string - read a string from a file
- X *
- X * SYNOPSIS
- X * string_ty *cache_read_string(FILE *fp));
- X *
- X * DESCRIPTION
- X * The cache_read_string function is used to read a string
- X * from a file.
- X *
- X * ARGUMENTS
- X * fp - file to read string from
- X *
- X * RETURNS
- X * pointer to string if successful, 0 if not.
- X *
- X * CAVEATS
- X * Must be symmetric with cache_write string below.
- X */
- X
- Xstatic string_ty *cache_read_string _((FILE *));
- X
- Xstatic string_ty *
- Xcache_read_string(fp)
- X FILE *fp;
- X{
- X static size_t buflen;
- X static char *buf;
- X size_t len;
- X
- X if (fread_sane(fp, &len, sizeof(len)))
- X return 0;
- X if (len > buflen)
- X {
- X buflen = (len + 0xFF) & ~0xFF;
- X if (!buf)
- X buf = mem_alloc(buflen);
- X else
- X mem_change_size(&buf, buflen);
- X }
- X if (fread_sane(fp, buf, len))
- X return 0;
- X return str_n_from_c(buf, len);
- X}
- X
- X
- X/*
- X * NAME
- X * cache_read_item - read a cache item from a file
- X *
- X * SYNOPSIS
- X * int cache_read_item(FILE *fp);
- X *
- X * DESCRIPTION
- X * The cache_read_item function is used to read an item from
- X * the cache file and installit into the cache.
- X *
- X * ARGUMENTS
- X * fp - the file to read the item from
- X *
- X * RETURNS
- X * 0 in success, -1 on any error
- X *
- X * CAVEATS
- X * Must be symmetric with cache_write_item below.
- X */
- X
- Xstatic int cache_read_item _((FILE *));
- X
- Xstatic int
- Xcache_read_item(fp)
- X FILE *fp;
- X{
- X string_ty *s;
- X cache_ty *cp;
- X size_t nitems;
- X size_t j;
- X
- X s = cache_read_string(fp);
- X if (!s)
- X return -1;
- X cp = cache_search(s);
- X assert(cp);
- X if (fread_sane(fp, &cp->st, sizeof(cp->st)))
- X return -1;
- X if (fread_sane(fp, &nitems, sizeof(nitems)))
- X return -1;
- X for (j = 0; j < nitems; ++j)
- X {
- X s = cache_read_string(fp);
- X if (!s)
- X return -1;
- X wl_append_unique(&cp->ingredients, s);
- X }
- X return 0;
- X}
- X
- X
- X/*
- X * NAME
- X * cache_read - read the cache file into the cache
- X *
- X * SYNOPSIS
- X * void cache_read(void);
- X *
- X * DESCRIPTION
- X * The cache_read function is used to read the cache file into the cache.
- X *
- X * CAVEATS
- X * If the cache file is not there, it is as iff the cache file
- X * contained an image of an empty cache. I.e. nothing happens,
- X * but it is not an error.
- X */
- X
- Xvoid
- Xcache_read()
- X{
- X str_hash_ty nitems;
- X str_hash_ty j;
- X FILE *fp;
- X char filename[256];
- X
- X /*
- X * open the cache file.
- X * if it's not there, quietly slink away
- X */
- X build_filename(filename);
- X if (!os_exists(filename))
- X return;
- X fp = fopen(filename, "rb");
- X if (!fp)
- X {
- X bomb:
- X nfatal("%s", filename);
- X }
- X
- X /*
- X * get the number of entries in the file
- X */
- X if (fread_sane(fp, &nitems, sizeof(nitems)))
- X goto bomb;
- X
- X /*
- X * read each entry in the file
- X */
- X for (j = 0; j < nitems; ++j)
- X {
- X if (cache_read_item(fp))
- X goto bomb;
- X }
- X
- X /*
- X * all done
- X */
- X fclose(fp);
- X}
- X
- X
- X/*
- X * NAME
- X * fwrite_sane - a saner version of fwrite
- X *
- X * SYNOPSIS
- X * int fwrite_sane(FILE *fp, void *buf, size_t buflen);
- X *
- X * DESCRIPTION
- X * The fwrite_sane function is used to write data to a file.
- X *
- X * ARGUMENTS
- X * fp - file to write to
- X * buf - pointer to data to write
- X * buflen - number of bytes in data
- X *
- X * RETURNS
- X * 0 on success, -1 on any error
- X */
- X
- Xstatic int fwrite_sane _((FILE *, void *, size_t));
- X
- Xstatic int
- Xfwrite_sane(fp, buf, buflen)
- X FILE *fp;
- X void *buf;
- X size_t buflen;
- X{
- X if (fwrite(buf, 1, buflen, fp) != buflen)
- X return -1;
- X return 0;
- X}
- X
- X
- X/*
- X * NAME
- X * cache_write_string - write a string to a file
- X *
- X * SYNOPSIS
- X * int cache_write_string(FILE *fp, string_ty *s);
- X *
- X * DESCRIPTION
- X * The cache_write_string function is used to write a string to a file.
- X *
- X * ARGUMENTS
- X * fp - file to write
- X * s - string to be written
- X *
- X * RETURNS
- X * 0 on success, -1 on any error
- X *
- X * CAVEATS
- X * Must be symmetric with cache_read_string above.
- X */
- X
- Xstatic int cache_write_string _((FILE *, string_ty *));
- X
- Xstatic int
- Xcache_write_string(fp, s)
- X FILE *fp;
- X string_ty *s;
- X{
- X if (fwrite_sane(fp, &s->str_length, sizeof(s->str_length)))
- X return -1;
- X if (fwrite_sane(fp, s->str_text, s->str_length))
- X return -1;
- X return 0;
- X}
- X
- X
- X/*
- X * NAME
- X * cache_write_item - write cache item to cache file
- X *
- X * SYNOPSIS
- X * int cache_write_item(FILE *fp, cache_ty *cp);
- X *
- X * DESCRIPTION
- X * The cache_write_item function is used to write a cache
- X * item to a cache file.
- X *
- X * ARGUMENTS
- X * fp - file to write
- X * cp - pointer to cache item to write
- X *
- X * RETURNS
- X * 0 on success, -1 on any error
- X *
- X * CAVEATS
- X * Must be symmetric with cache_read_item above.
- X */
- X
- Xstatic int cache_write_item _((FILE *, cache_ty *));
- X
- Xstatic int
- Xcache_write_item(fp, cp)
- X FILE *fp;
- X cache_ty *cp;
- X{
- X size_t j;
- X
- X if (cache_write_string(fp, cp->filename))
- X return -1;
- X if (fwrite_sane(fp, &cp->st, sizeof(cp->st)))
- X return -1;
- X if (fwrite_sane(fp, &cp->ingredients.wl_nwords, sizeof(cp->ingredients.wl_nwords)))
- X return -1;
- X for (j = 0; j < cp->ingredients.wl_nwords; ++j)
- X if (cache_write_string(fp, cp->ingredients.wl_word[j]))
- X return -1;
- X return 0;
- X}
- X
- X
- X/*
- X * NAME
- X * cache_write - write cache to file
- X *
- X * SYNOPSIS
- X * void cache_write(void);
- X *
- X * DESCRIPTION
- X * The cache_write function is used to write the memory image
- X * of the cache into a disk file.
- X *
- X * CAVEATS
- X * The cache file is in the current directory.
- X */
- X
- Xvoid
- Xcache_write()
- X{
- X node *p;
- X FILE *fp;
- X char filename[256];
- X str_hash_ty index;
- X
- X /*
- X * don't change the file if we don't have to
- X */
- X if (!need_to_write)
- X return;
- X need_to_write = 0;
- X
- X /*
- X * open the cache file
- X */
- X build_filename(filename);
- X fp = fopen(filename, "wb");
- X if (!fp)
- X {
- X bomb:
- X nfatal("%s", filename);
- X }
- X
- X /*
- X * write the number of entries to the file
- X */
- X if (fwrite_sane(fp, &hash_load, sizeof(hash_load)))
- X goto bomb;
- X
- X /*
- X * write each cache entry to the file
- X */
- X for (index = 0; index < hash_modulus; ++index)
- X {
- X for (p = hash_table[index]; p; p = p->next)
- X {
- X if (cache_write_item(fp, &p->cache))
- X goto bomb;
- X }
- X }
- X
- X /*
- X * close the cache file
- X */
- X if (fclose(fp))
- X goto bomb;
- X}
- X
- X
- X/*
- X * NAME
- X * cache_update_notify - cache has changed
- X *
- X * SYNOPSIS
- X * void cache_update_nitify(void);
- X *
- X * DESCRIPTION
- X * The cache_update_notify function is called whenever the contents
- X * of the cache is changed. This notifies the cache_write function
- X * that it needs to rewrite the cache file.
- X */
- X
- Xvoid
- Xcache_update_notify()
- X{
- X need_to_write = 1;
- X}
- END_OF_FILE
- if test 12450 -ne `wc -c <'c_incl/cache.c'`; then
- echo shar: \"'c_incl/cache.c'\" unpacked with wrong size!
- fi
- # end of 'c_incl/cache.c'
- fi
- if test -f 'c_incl/main.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'c_incl/main.c'\"
- else
- echo shar: Extracting \"'c_incl/main.c'\" \(11963 characters\)
- sed "s/^X//" >'c_incl/main.c' <<'END_OF_FILE'
- X/*
- X * cook - file construction tool
- X * Copyright (C) 1991, 1992, 1993 Peter Miller.
- X * All rights reserved.
- X *
- X * This program is free software; you can redistribute it and/or modify
- X * it under the terms of the GNU General Public License as published by
- X * the Free Software Foundation; either version 2 of the License, or
- X * (at your option) any later version.
- X *
- X * This program is distributed in the hope that it will be useful,
- X * but WITHOUT ANY WARRANTY; without even the implied warranty of
- X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- X * GNU General Public License for more details.
- X *
- X * You should have received a copy of the GNU General Public License
- X * along with this program; if not, write to the Free Software
- X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- X *
- X * MANIFEST: operating system entry point, and command line argument parsing
- X */
- X
- X#include <stddef.h>
- X#include <stdio.h>
- X#include <string.h>
- X#include <stdlib.h>
- X
- X#include <arglex.h>
- X#include <cache.h>
- X#include <error.h>
- X#include <help.h>
- X#include <main.h>
- X#include <sniff.h>
- X#include <str.h>
- X#include <trace.h>
- X#include <version.h>
- X
- X
- Xstatic void usage _((void));
- X
- Xstatic void
- Xusage()
- X{
- X fprintf(stderr, "usage: %s [ <option>... ] <filename>\n", progname);
- X fprintf(stderr, " %s -Help\n", progname);
- X fprintf(stderr, " %s -VERsion\n", progname);
- X exit(1);
- X}
- X
- X
- Xstatic void c_incl_help _((void));
- X
- Xstatic void
- Xc_incl_help()
- X{
- X static char *text[] =
- X {
- X"NAME",
- X" %s - determine dependencies",
- X"",
- X"SYNOPSIS",
- X" %s [ <option>... ] <filename>",
- X" %s -Help",
- X" %s -VERSion",
- X"",
- X"DESCRIPTION",
- X" The %s program is used to traverse source files",
- X" looking for include dependencies suitable for",
- X" [collect]ion by cook.",
- X"",
- X" Several input languages are supported, see the options",
- X" list for details.",
- X"",
- X"OPTIONS",
- X" The following options are understood.",
- X"",
- X" -C",
- X" The source file is a C source file. It is",
- X" assumed that it will have the dependencies",
- X" resolved by the cpp(1) command. The same include",
- X" semantics as the cpp(1) command will be employed.",
- X" This is the default.",
- X"",
- X" -Roff",
- X" The source file is a *roff source file. It is",
- X" assumed that it will have the dependencies",
- X" resolved by the roffpp(1) command. The same",
- X" include semantics as the roffpp(1) command will",
- X" be employed.",
- X"",
- X" -Verbose",
- X" Tell what is happening.",
- X"",
- X" -I<path>",
- X" Specify include path, a la cc(1).",
- X"",
- X" -Absent_Local_Ignore",
- X" For files included using a #include",
- X" ''filename.h'' directive, ignore the file if it",
- X" cannot be found.",
- X"",
- X" -Absent_Local_Mention",
- X" For files included using a #include",
- X" ''filename.h'' directive, print the file name",
- X" even if the file cannot be found. This is the",
- X" default (it probably needs to be built).",
- X"",
- X" -Absent_Local_Error",
- X" For files included using a #include",
- X" ''filename.h'' directive, print a fatal error if",
- X" the file cannot be found.",
- X"",
- X" -Absent_System_Ignore",
- X" For files included with a #include <filename.h>",
- X" directive, ignore the file if it cannot be found.",
- X" This is the default (it was probably ifdef'ed",
- X" out).",
- X"",
- X" -Absent_System_Mention",
- X" For files included with a #include <filename.h>",
- X" directive, print the file name even if the file",
- X" cannot be found.",
- X"",
- X" -Absent_System_Error",
- X" For files included with a #include <filename.h>",
- X" directive, print a fatal error if the file cannot",
- X" be found.",
- X"",
- X" -Absent_Program_Ignore",
- X" If the file named on the command line cannot be",
- X" found, behave as if the file were found, but was",
- X" empty.",
- X"",
- X" -Absent_Program_Error",
- X" If the file named on the command line cannot be",
- X" found, print a fatal error message. This is the",
- X" default.",
- X"",
- X" -Help",
- X" Give information on how to use %s.",
- X"",
- X" -VERSion",
- X" Tell what version of %s is being run.",
- X"",
- X" -No_System",
- X" Do not search the /usr/include directory. By",
- X" default this is searched last.",
- X"",
- X" -No_Cache",
- X" This option may be used to turn caching off.",
- X"",
- X" Any other options will generate an error.",
- X"",
- X" All options may be abbreviated; the abbreviation is",
- X" documented as the upper case letters, all lower case",
- X" letters and underscores (_) are optional. You must use",
- X" consecutive sequences of optional letters.",
- X"",
- X" All options are case insensitive, you may type them in",
- X" upper case or lower case or a combination of both, case",
- X" is not important.",
- X"",
- X" For example: the arguments \"-help\", \"-HEL\" and \"-h\" are",
- X" all interpreted to mean the -Help option. The argument",
- X" \"-hlp\" will not be understood, because consecutive",
- X" optional characters were not supplied.",
- X"",
- X" Options and other command line arguments may be mixed",
- X" arbitrarily on the command line.",
- X"",
- X" The GNU long option names are understood. Since all",
- X" option names for %s are long, this means ignoring the",
- X" extra leading '-'. The \"--option=value\" convention is",
- X" also understood.",
- X"",
- X"CACHING",
- X" The caching mechanism use by the %s program caches",
- X" the results of searching files for include files (in a",
- X" file called .%src in the current directory). The",
- X" cache is only refreshed when a file changes.",
- X"",
- X" The use of this cache has been shown to dramatically",
- X" increase the performance of the %s program.",
- X" Typically, only a small proportions files in a project",
- X" change between builds, resulting in a very high cache hit",
- X" rate.",
- X"",
- X" When using caching, always use the same command line",
- X" options, otherwise weird and wonderful things will",
- X" happen.",
- X"",
- X" The .%src file is a binary file. If you wish to",
- X" rebuild the cache, simply delete this file with the rm(1)",
- X" command. Being a binary file, the .%src file is not",
- X" portable across machines or operating systems, so you",
- X" will need to delete it when you move your sources. It is",
- X" a binary file for performance.",
- X"",
- X"EXIT STATUS",
- X" The %s command will exit with a status of 1 on any",
- X" error. The %s command will only exit with a status",
- X" of 0 if there are no errors.",
- X"",
- X"COPYRIGHT",
- X" %C",
- X"",
- X"AUTHOR",
- X" %A",
- X };
- X
- X trace(("c_incl_help()\n{\n"/*}*/));
- X help(text, SIZEOF(text), usage);
- X trace((/*{*/"}\n"));
- X}
- X
- X
- Xenum
- X{
- X arglex_token_absent_local_error,
- X arglex_token_absent_local_ignore,
- X arglex_token_absent_local_mention,
- X arglex_token_absent_program_error,
- X arglex_token_absent_program_ignore,
- X arglex_token_absent_system_error,
- X arglex_token_absent_system_ignore,
- X arglex_token_absent_system_mention,
- X arglex_token_include,
- X arglex_token_no_cache,
- X arglex_token_no_system,
- X arglex_token_lang_c,
- X arglex_token_lang_roff,
- X arglex_token_verbose
- X};
- X
- Xstatic arglex_table_ty argtab[] =
- X{
- X {
- X "-Absent",
- X (arglex_token_ty)arglex_token_absent_local_ignore,
- X },
- X {
- X "-Absent_Local_Error",
- X (arglex_token_ty)arglex_token_absent_local_error,
- X },
- X {
- X "-Absent_Local_Ignore",
- X (arglex_token_ty)arglex_token_absent_local_ignore,
- X },
- X {
- X "-Absent_Local_Mention",
- X (arglex_token_ty)arglex_token_absent_local_mention,
- X },
- X {
- X "-Absent_Program_Error",
- X (arglex_token_ty)arglex_token_absent_program_error,
- X },
- X {
- X "-Absent_Program_Ignore",
- X (arglex_token_ty)arglex_token_absent_program_ignore,
- X },
- X {
- X "-Absent_System_Error",
- X (arglex_token_ty)arglex_token_absent_system_error,
- X },
- X {
- X "-Absent_System_Ignore",
- X (arglex_token_ty)arglex_token_absent_system_ignore,
- X },
- X {
- X "-Absent_System_Mention",
- X (arglex_token_ty)arglex_token_absent_system_mention,
- X },
- X {
- X "-C",
- X (arglex_token_ty)arglex_token_lang_c,
- X },
- X {
- X "-Empty_If_Absent",
- X (arglex_token_ty)arglex_token_absent_program_ignore,
- X },
- X {
- X "-\\I*",
- X (arglex_token_ty)arglex_token_include,
- X },
- X {
- X "-Include",
- X (arglex_token_ty)arglex_token_include,
- X },
- X {
- X "-No_Cache",
- X (arglex_token_ty)arglex_token_no_cache,
- X },
- X {
- X "-No_System",
- X (arglex_token_ty)arglex_token_no_system,
- X },
- X {
- X "-Roff",
- X (arglex_token_ty)arglex_token_lang_roff,
- X },
- X {
- X "-System_absent",
- X (arglex_token_ty)arglex_token_absent_system_ignore,
- X },
- X {
- X "-Verbose",
- X (arglex_token_ty)arglex_token_verbose,
- X },
- X
- X /* end marker */
- X { 0, (arglex_token_ty)0, },
- X};
- X
- X
- Xint main _((int, char **));
- X
- Xint
- Xmain(argc, argv)
- X int argc;
- X char **argv;
- X{
- X char *source;
- X int no_system;
- X int no_cache;
- X sniff_ty *language;
- X
- X arglex_init(argc, argv, argtab);
- X str_initialize();
- X cache_initialize();
- X switch (arglex())
- X {
- X case arglex_token_help:
- X c_incl_help();
- X exit(0);
- X
- X case arglex_token_version:
- X version();
- X exit(0);
- X
- X default:
- X break;
- X }
- X
- X source = 0;
- X no_system = 0;
- X no_cache = 0;
- X language = 0;
- X option.o_absent_local = -1;
- X option.o_absent_system = -1;
- X option.o_absent_program = -1;
- X while (arglex_token != arglex_token_eoln)
- X {
- X switch(arglex_token)
- X {
- X default:
- X error
- X (
- X "misplaced \"%s\" command line argument",
- X arglex_value.alv_string
- X );
- X usage();
- X
- X case arglex_token_string:
- X if (source)
- X fatal("too many filenames specified");
- X source = arglex_value.alv_string;
- X break;
- X
- X case arglex_token_verbose:
- X if (option.o_verbose)
- X {
- X duplicate:
- X fatal
- X (
- X "duplicate \"%s\" option",
- X arglex_value.alv_string
- X );
- X }
- X ++option.o_verbose;
- X break;
- X
- X case arglex_token_absent_local_ignore:
- X if (option.o_absent_local != -1)
- X goto duplicate;
- X option.o_absent_local = absent_ignore;
- X break;
- X
- X case arglex_token_absent_local_mention:
- X if (option.o_absent_local != -1)
- X goto duplicate;
- X option.o_absent_local = absent_mention;
- X break;
- X
- X case arglex_token_absent_local_error:
- X if (option.o_absent_local != -1)
- X goto duplicate;
- X option.o_absent_local = absent_error;
- X break;
- X
- X case arglex_token_absent_system_ignore:
- X if (option.o_absent_system != -1)
- X goto duplicate;
- X option.o_absent_system = absent_ignore;
- X break;
- X
- X case arglex_token_absent_system_mention:
- X if (option.o_absent_system != -1)
- X goto duplicate;
- X option.o_absent_system = absent_mention;
- X break;
- X
- X case arglex_token_absent_system_error:
- X if (option.o_absent_system != -1)
- X goto duplicate;
- X option.o_absent_system = absent_error;
- X break;
- X
- X case arglex_token_absent_program_ignore:
- X if (option.o_absent_program != -1)
- X goto duplicate;
- X option.o_absent_program = absent_ignore;
- X break;
- X
- X case arglex_token_absent_program_error:
- X if (option.o_absent_program != -1)
- X goto duplicate;
- X option.o_absent_program = absent_error;
- X break;
- X
- X case arglex_token_include:
- X if (arglex() != arglex_token_string)
- X fatal("-Include requires a string argument");
- X sniff_include(arglex_value.alv_string);
- X break;
- X
- X case arglex_token_no_system:
- X if (no_system)
- X goto duplicate;
- X no_system++;
- X break;
- X
- X case arglex_token_no_cache:
- X if (no_cache)
- X goto duplicate;
- X no_cache++;
- X break;
- X
- X case arglex_token_lang_c:
- X if (language)
- X goto duplicate;
- X language = &lang_c;
- X break;
- X
- X case arglex_token_lang_roff:
- X if (language)
- X goto duplicate;
- X language = &lang_roff;
- X break;
- X
- X#ifdef DEBUG
- X case arglex_token_tracing:
- X if (arglex() != arglex_token_string)
- X fatal("-TRACIng requires one or more string arguments");
- X for (;;)
- X {
- X trace_enable(arglex_value.alv_string);
- X if (arglex() != arglex_token_string)
- X break;
- X }
- X continue;
- X#endif
- X }
- X arglex();
- X }
- X if (option.o_absent_local == -1)
- X option.o_absent_local = absent_mention;
- X if (option.o_absent_system == -1)
- X option.o_absent_system = absent_ignore;
- X if (option.o_absent_program == -1)
- X option.o_absent_program = absent_error;
- X if (!source)
- X fatal("no input file specified");
- X
- X /*
- X * set the language to be used
- X */
- X if (!language)
- X language = &lang_c;
- X sniff_language(language);
- X
- X /*
- X * apply any default or suffix search rules
- X * or anything else defined by the language
- X */
- X if (!no_system)
- X sniff_prepare();
- X
- X /*
- X * read and analyze the file
- X */
- X if (!no_cache)
- X cache_read();
- X sniff(source);
- X if (!no_cache)
- X cache_write();
- X exit(0);
- X return 0;
- X}
- END_OF_FILE
- if test 11963 -ne `wc -c <'c_incl/main.c'`; then
- echo shar: \"'c_incl/main.c'\" unpacked with wrong size!
- fi
- # end of 'c_incl/main.c'
- fi
- if test -f 'c_incl/sniff.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'c_incl/sniff.c'\"
- else
- echo shar: Extracting \"'c_incl/sniff.c'\" \(11352 characters\)
- sed "s/^X//" >'c_incl/sniff.c' <<'END_OF_FILE'
- X/*
- X * cook - file construction tool
- X * Copyright (C) 1991, 1992, 1993 Peter Miller.
- X * All rights reserved.
- X *
- X * This program is free software; you can redistribute it and/or modify
- X * it under the terms of the GNU General Public License as published by
- X * the Free Software Foundation; either version 2 of the License, or
- X * (at your option) any later version.
- X *
- X * This program is distributed in the hope that it will be useful,
- X * but WITHOUT ANY WARRANTY; without even the implied warranty of
- X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- X * GNU General Public License for more details.
- X *
- X * You should have received a copy of the GNU General Public License
- X * along with this program; if not, write to the Free Software
- X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- X *
- X * MANIFEST: functions to scan source files looking for include files
- X */
- X
- X#include <stddef.h>
- X#include <stdio.h>
- X#include <string.h>
- X#include <errno.h>
- X
- X#include <cache.h>
- X#include <error.h>
- X#include <mem.h>
- X#include <os.h>
- X#include <sniff.h>
- X#include <word.h>
- X#include <trace.h>
- X
- Xoption_ty option;
- X
- Xstatic wlist srl;
- Xstatic wlist visited;
- Xstatic sniff_ty *lang;
- X
- X
- Xvoid
- Xsniff_language(lp)
- X sniff_ty *lp;
- X{
- X trace(("sniff_language(lp = %08lX)\n{\n"/*}*/, lp));
- X assert(lp);
- X lang = lp;
- X trace((/*{*/"}\n"));
- X}
- X
- X
- X/*
- X * NAME
- X * sniff_include
- X *
- X * SYNOPSIS
- X * void sniff_include(string_ty *path);
- X *
- X * DESCRIPTION
- X * The sniff_include function is used to add to
- X * the standard include paths.
- X *
- X * ARGUMENTS
- X * path - path to add
- X */
- X
- Xvoid
- Xsniff_include(path)
- X char *path;
- X{
- X string_ty *s;
- X
- X assert(path);
- X trace(("sniff_include(path = \"%s\")\n{\n"/*}*/, path));
- X s = str_from_c(path);
- X wl_append_unique(&srl, s);
- X str_free(s);
- X trace((/*{*/"}\n"));
- X}
- X
- X
- Xlong
- Xsniff_include_count()
- X{
- X trace(("sniff_include_count()\n{\n"/*}*/));
- X trace(("return %ld;\n", (long)srl.wl_nwords));
- X trace((/*{*/"}\n"));
- X return srl.wl_nwords;
- X}
- X
- X
- X/*
- X * NAME
- X * sniff_prepare
- X *
- X * SYNOPSIS
- X * void sniff_prepare(void);
- X *
- X * DESCRIPTION
- X * The sniff_prepare function is used to append the standard
- X * search paths after the user-supplied search paths.
- X */
- X
- Xvoid
- Xsniff_prepare()
- X{
- X trace(("sniff_prepare()\n{\n"/*}*/));
- X assert(lang);
- X lang->prepare();
- X trace((/*{*/"}\n"));
- X}
- X
- X
- X/*
- X * NAME
- X * flatten - remove kinks from paths
- X *
- X * SYNOPSIS
- X * void flatten(char *);
- X *
- X * DESCRIPTION
- X * The flatten function is used to remove kinks
- X * from unix path named.
- X *
- X * ARGUMENTS
- X * path - path to flatten
- X *
- X * CAVEATS
- X * It doesn't understand symbolic links.
- X */
- X
- Xstatic string_ty *flatten _((string_ty *));
- X
- Xstatic string_ty *
- Xflatten(s)
- X string_ty *s;
- X{
- X static size_t tmplen;
- X static char *tmp;
- X char *in;
- X char *out;
- X
- X /*
- X * make sure tmp area is big enough
- X */
- X trace(("flatten(s = \"%s\")\n{\n"/*}*/, s));
- X if (s->str_length > tmplen)
- X {
- X if (!tmplen)
- X {
- X tmplen = s->str_length;
- X tmp = mem_alloc(tmplen + 1);
- X }
- X else
- X {
- X tmplen = s->str_length;
- X mem_change_size(&tmp, tmplen + 1);
- X }
- X }
- X
- X /*
- X * remove multiple '/'s
- X */
- X out = tmp;
- X for (in = s->str_text; *in; ++in)
- X {
- X if (in[0] != '/' || in[1] != '/')
- X *out++ = *in;
- X }
- X *out = 0;
- X
- X /*
- X * remove . references
- X */
- X in = out = tmp;
- X if (*in == '/')
- X *out++ = *in++;
- X while (*in)
- X {
- X if (in[0] == '.' && !in[1])
- X break;
- X if (in[0] == '.' && in[1] == '/')
- X {
- X in += 2;
- X continue;
- X }
- X while (*in && (*out++ = *in++) != '/')
- X ;
- X }
- X *out = 0;
- X
- X /*
- X * remove .. references
- X */
- X in = out = tmp;
- X if (*in == '/')
- X *out++ = *in++;
- X while (*in)
- X {
- X if (in[0] == '.' && in[1] == '.' && !in[2])
- X {
- X if (out == tmp)
- X {
- X /* ".." -> ".." */
- X *out++ = *in++;
- X *out++ = *in++;
- X break;
- X }
- X if (out == tmp + 1)
- X {
- X /* "/.." -> "/" */
- X break;
- X }
- X --out;
- X while (out > tmp && out[-1] != '/')
- X --out;
- X break;
- X }
- X if (in[0] == '.' && in[1] == '.' && in[2] == '/')
- X {
- X if (out == tmp)
- X {
- X /* "../" -> "../" */
- X *out++ = *in++;
- X *out++ = *in++;
- X *out++ = *in++;
- X continue;
- X }
- X if (out == tmp + 1)
- X {
- X /* "/../" -> "/" */
- X in += 3;
- X continue;
- X }
- X --out;
- X while (out > tmp && out[-1] != '/')
- X --out;
- X continue;
- X }
- X while (*in && (*out++ = *in++) != '/')
- X ;
- X }
- X *out = 0;
- X
- X /*
- X * remove trailing '/'
- X */
- X if (out > tmp + 1 && out[-1] == '/')
- X *--out = 0;
- X
- X /*
- X * "" -> "."
- X */
- X if (!*tmp)
- X {
- X tmp[0] = '.';
- X tmp[1] = 0;
- X }
- X
- X /*
- X * return a string
- X */
- X trace(("return \"%s\";\n", tmp));
- X trace((/*{*/"}\n"));
- X return str_from_c(tmp);
- X}
- X
- X
- X/*
- X * NAME
- X * resolve
- X *
- X * SYNOPSIS
- X * string_ty *resolve(string_ty *filename, string_ty *extra);
- X *
- X * DESCRIPTION
- X * The resolve function is used to resolve an include
- X * filename into the path of an existing file.
- X *
- X * ARGUMENTS
- X * filename - name to be resolved
- X * extra - extra first search element, if not NULL
- X *
- X * RETURNS
- X * string_ty *; name of path, or NULL if unmentionable
- X */
- X
- Xstatic string_ty *resolve _((string_ty *filename, string_ty *extra));
- X
- Xstatic string_ty *
- Xresolve(filename, extra)
- X string_ty *filename;
- X string_ty *extra;
- X{
- X string_ty *s;
- X size_t j;
- X string_ty *result;
- X
- X /*
- X * If the name is absolute, irrespecitive of
- X * which style, we need look no further.
- X */
- X trace(("resolve(filename = \"%s\", extra = \"%s\")\n{\n"/*}*/,
- X filename->str_text, extra ? extra->str_text : "NULL"));
- X if (filename->str_text[0] == '/')
- X {
- X result = flatten(filename);
- X if (os_exists(result->str_text))
- X goto done;
- X goto dilema;
- X }
- X
- X /*
- X * Includes of the form "filename" look in the directory
- X * of the parent file, an then the <filename> places.
- X */
- X if (extra)
- X {
- X s = str_format("%S/%S", extra, filename);
- X result = flatten(s);
- X str_free(s);
- X if (option.o_verbose)
- X error("may need to look at \"%s\"", result->str_text);
- X if (os_exists(result->str_text))
- X goto done;
- X str_free(result);
- X }
- X
- X /*
- X * look in all the standard places
- X */
- X for (j = 0; j < srl.wl_nwords; ++j)
- X {
- X s = str_format("%S/%S", srl.wl_word[j], filename);
- X result = flatten(s);
- X str_free(s);
- X if (option.o_verbose)
- X error("may need to look at \"%s\"", result->str_text);
- X if (os_exists(result->str_text))
- X goto done;
- X str_free(result);
- X }
- X
- X /*
- X * not found, must have been ifdef'ed out
- X * or needs to be built
- X */
- X dilema:
- X switch (extra ? option.o_absent_local : option.o_absent_system)
- X {
- X default:
- X fatal("include file \"%s\" not found", filename->str_text);
- X
- X case absent_ignore:
- X result = 0;
- X break;
- X
- X case absent_mention:
- X if (extra)
- X {
- X s = str_format("%S/%S", extra, filename);
- X result = flatten(s);
- X str_free(s);
- X }
- X else
- X result = flatten(filename);
- X break;
- X }
- X
- X /*
- X * here for all exits
- X */
- X done:
- X trace(("return \"%s\";\n", result ? result->str_text : "NULL"));
- X trace((/*{*/"}\n"));
- X return result;
- X}
- X
- X
- X/*
- X * NAME
- X * stat_equal - compare stat structures
- X *
- X * SYNOPSIS
- X * vint stat_equal(struct stat *, struct stat *);
- X *
- X * DESCRIPTION
- X * The stat_equal function is sued to compare two stat structures
- X * for equality. Only this fields which would change if
- X * the file changed are examined.
- X */
- X
- Xstatic int stat_equal _((struct stat *, struct stat *));
- X
- Xstatic int
- Xstat_equal(st1, st2)
- X struct stat *st1;
- X struct stat *st2;
- X{
- X return
- X (
- X st1->st_dev == st2->st_dev
- X &&
- X st1->st_ino == st2->st_ino
- X &&
- X st1->st_size == st2->st_size
- X &&
- X st1->st_mtime == st2->st_mtime
- X &&
- X st1->st_ctime == st2->st_ctime
- X );
- X}
- X
- X
- X/*
- X * NAME
- X * sniffer - search file for include dependencies
- X *
- X * SYNOPSIS
- X * void sniffer(string_ty *pathname);
- X *
- X * DESCRIPTION
- X * The sniffer function is used to walk a file looking
- X * for any files which it includes, and walking then also.
- X * The names of any include files encountered are printed onto
- X * the standard output.
- X *
- X * ARGUMENTS
- X * pathname - pathname to read
- X *
- X * CAVEATS
- X * Uses the cache where possible to speed things up.
- X */
- X
- Xstatic void sniffer _((string_ty *));
- X
- Xstatic void
- Xsniffer(filename)
- X string_ty *filename;
- X{
- X FILE *fp;
- X cache_ty *cp;
- X struct stat st;
- X size_t j;
- X
- X /*
- X * find the file in the cache
- X * (will be created if not already there)
- X */
- X trace(("sniffer(filename = \"%s\")\n{\n"/*}*/, filename->str_text));
- X cp = cache_search(filename);
- X assert(cp);
- X if (stat(filename->str_text, &st) < 0)
- X {
- X /*
- X * here for failure to open/find a file
- X */
- X absent:
- X switch (errno)
- X {
- X case ENOENT:
- X break;
- X
- X case ENOTDIR:
- X case EACCES:
- X nerror("%s (warning)", filename->str_text);
- X break;
- X
- X default:
- X nfatal("%s", filename->str_text);
- X }
- X
- X /*
- X * zap the stat info,
- X * and pretend the file was empty
- X */
- X memset(&cp->st, 0, sizeof(cp->st));
- X wl_free(&cp->ingredients);
- X cache_update_notify();
- X if (option.o_verbose)
- X error("bogus empty \"%s\" file", filename->str_text);
- X goto done;
- X }
- X
- X /*
- X * if the stat in the cache is not the same
- X * as the state just obtained, reread the file.
- X */
- X if (!stat_equal(&st, &cp->st))
- X {
- X wlist type1;
- X wlist type2;
- X
- X cp->st = st;
- X wl_free(&cp->ingredients);
- X cache_update_notify();
- X if (option.o_verbose)
- X error("cache miss for \"%s\" file", filename->str_text);
- X
- X fp = fopen(filename->str_text, "r");
- X if (!fp)
- X {
- X /*
- X * probably "permission denied" error,
- X * but file could have suddenly vanished
- X */
- X goto absent;
- X }
- X wl_zero(&type1);
- X wl_zero(&type2);
- X if (lang->scan(fp, &type1, &type2) || fclose(fp))
- X nfatal("%s", filename->str_text);
- X
- X /*
- X * type2 names have an implicit first element of the search path
- X * which is the directory of the including file
- X */
- X if (type2.wl_nwords)
- X {
- X string_ty *parent;
- X char *ep;
- X char *sp;
- X
- X sp = filename->str_text;
- X ep = strrchr(sp, '/');
- X if (ep)
- X parent = str_n_from_c(sp, ep - sp);
- X else
- X parent = str_from_c(".");
- X for (j = 0; j < type2.wl_nwords; ++j)
- X {
- X string_ty *path;
- X
- X path = resolve(type2.wl_word[j], parent);
- X if (path)
- X {
- X wl_append_unique(&cp->ingredients, path);
- X str_free(path);
- X }
- X }
- X str_free(parent);
- X }
- X
- X /*
- X * type1 names scan the search path
- X */
- X for (j = 0; j < type1.wl_nwords; ++j)
- X {
- X string_ty *path;
- X
- X path = resolve(type1.wl_word[j], (string_ty *)0);
- X if (path)
- X {
- X wl_append_unique(&cp->ingredients, path);
- X str_free(path);
- X }
- X }
- X
- X /*
- X * let the lists go
- X */
- X wl_free(&type1);
- X wl_free(&type2);
- X }
- X
- X /*
- X * work down the ingredients list
- X * to see if there are more dependencies
- X */
- X wl_append_unique(&visited, filename);
- X for (j = 0; j < cp->ingredients.wl_nwords; ++j)
- X {
- X string_ty *s;
- X
- X s = cp->ingredients.wl_word[j];
- X if (!wl_member(&visited, s))
- X {
- X printf("%s\n", s->str_text);
- X sniffer(s);
- X }
- X }
- X
- X /*
- X * here for all exits
- X */
- X done:
- X trace((/*{*/"}\n"));
- X}
- X
- X
- X/*
- X * NAME
- X * sniff - search file for include dependencies
- X *
- X * SYNOPSIS
- X * void sniff(char *pathname);
- X *
- X * DESCRIPTION
- X * The sniff function is used to walk a file looking
- X * for any files which it includes, and walking then also.
- X * The names of any include files encountered are printed onto
- X * the standard output.
- X *
- X * ARGUMENTS
- X * pathname - pathname to read
- X */
- X
- Xvoid
- Xsniff(filename)
- X char *filename;
- X{
- X string_ty *s;
- X
- X assert(filename);
- X trace(("sniff(filename = \"%s\")\n{\n"/*}*/, filename));
- X if (!os_exists(filename) && option.o_absent_program == absent_error)
- X fatal("%s: no such file", filename);
- X s = str_from_c(filename);
- X sniffer(s);
- X str_free(s);
- X trace((/*{*/"}\n"));
- X}
- END_OF_FILE
- if test 11352 -ne `wc -c <'c_incl/sniff.c'`; then
- echo shar: \"'c_incl/sniff.c'\" unpacked with wrong size!
- fi
- # end of 'c_incl/sniff.c'
- fi
- if test -f 'common/arglex.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'common/arglex.c'\"
- else
- echo shar: Extracting \"'common/arglex.c'\" \(11998 characters\)
- sed "s/^X//" >'common/arglex.c' <<'END_OF_FILE'
- X/*
- X * cook - file construction tool
- X * Copyright (C) 1990, 1991, 1992, 1993 Peter Miller.
- X * All rights reserved.
- X *
- X * This program is free software; you can redistribute it and/or modify
- X * it under the terms of the GNU General Public License as published by
- X * the Free Software Foundation; either version 2 of the License, or
- X * (at your option) any later version.
- X *
- X * This program is distributed in the hope that it will be useful,
- X * but WITHOUT ANY WARRANTY; without even the implied warranty of
- X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- X * GNU General Public License for more details.
- X *
- X * You should have received a copy of the GNU General Public License
- X * along with this program; if not, write to the Free Software
- X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- X *
- X * MANIFEST: functions to perform lexical analysis on command line arguments
- X */
- X
- X#include <stddef.h>
- X#include <stdlib.h>
- X#include <string.h>
- X#include <ctype.h>
- X
- X#include <main.h>
- X#include <arglex.h>
- X#include <error.h>
- X#include <word.h>
- X#include <mem.h>
- X#include <trace.h>
- X
- Xstatic arglex_table_ty table[] =
- X{
- X { "-", arglex_token_stdio, },
- X { "-Help", arglex_token_help, },
- X { "-TRACIng", arglex_token_tracing, },
- X { "-VERSion", arglex_token_version, },
- X};
- X
- Xstatic int argc;
- Xstatic char **argv;
- Xarglex_value_ty arglex_value;
- Xarglex_token_ty arglex_token;
- Xchar *progname;
- Xstatic arglex_table_ty *utable;
- Xstatic char *partial;
- X
- X
- Xstatic char *basename _((char *));
- X
- Xstatic char *
- Xbasename(s)
- X char *s;
- X{
- X char *bp;
- X char *ep;
- X
- X bp = s;
- X ep = 0;
- X while (*s)
- X {
- X if (s[0] == '/' && s[1] && s[1] != '/')
- X bp = s + 1;
- X if (s > bp && s[0] == '/' && s[-1] != '/')
- X ep = s;
- X ++s;
- X }
- X if (!ep)
- X ep = s;
- X *s = 0;
- X return bp;
- X}
- X
- X
- X/*
- X * NAME
- X * arglex_init
- X *
- X * SYNOPSIS
- X * void arglex_init(int ac, char **av, arglex_table-t *tp);
- X *
- X * DESCRIPTION
- X * The arglex_init function is used to initialize the
- X * command line processing.
- X *
- X * ARGUMENTS
- X * ac - aergument count, from main
- X * av - argument values, from main
- X * tp - pointer to table of options
- X *
- X * CAVEAT
- X * Must be called before the arglex() function.
- X */
- X
- Xvoid
- Xarglex_init(ac, av, tp)
- X int ac;
- X char **av;
- X arglex_table_ty *tp;
- X{
- X progname = basename(av[0]);
- X
- X argc = ac - 1;
- X argv = av + 1;
- X utable = tp;
- X}
- X
- X
- X/*
- X * NAME
- X * arglex_compare
- X *
- X * SYNOPSIS
- X * int arglex_compare(char *formal, char *actual);
- X *
- X * DESCRIPTION
- X * The arglex_compare function is used to compare
- X * a command line string with a formal spec of the option,
- X * to see if they compare equal.
- X *
- X * The actual is case-insensitive. Uppercase in the formal
- X * means a mandatory character, while lower case means optional.
- X * Any number of consecutive optional characters may be supplied
- X * by actual, but none may be skipped, unless all are skipped to
- X * the next non-lower-case letter.
- X *
- X * The underscore (_) is like a lower-case minus,
- X * it matches "", "-" and "_".
- X *
- X * The "*" in a pattern matches everything to the end of the line,
- X * anything after the "*" is ignored. The rest of the line is pointed
- X * to by the "partial" variable as a side-effect (else it will be 0).
- X * This rather ugly feature is to support "-I./dir" type options.
- X *
- X * A backslash in a pattern nominates an exact match required,
- X * case must matche excatly here.
- X * This rather ugly feature is to support "-I./dir" type options.
- X *
- X * For example: "-project" and "-P' both match "-Project",
- X * as does "-proJ", but "-prj" does not.
- X *
- X * For example: "-devDir" and "-d_d' both match "-Development_Directory",
- X * but "-dvlpmnt_drctry" does not.
- X *
- X * For example: to match include path specifications, use a pattern
- X * such as "-\\I*", and the partial global variable will have the
- X * path in it on return.
- X *
- X * ARGUMENTS
- X * formal - the "pattern" for the option
- X * actual - what the user supplied
- X *
- X * RETURNS
- X * int; zero if no match,
- X * non-zero if they do match.
- X */
- X
- Xint
- Xarglex_compare(formal, actual)
- X char *formal;
- X char *actual;
- X{
- X char fc;
- X char ac;
- X int result;
- X
- X trace(("arglex_compare(formal = \"%s\", actual = \"%s\")\n{\n",
- X formal, actual));
- X for (;;)
- X {
- X trace_string(formal);
- X trace_string(actual);
- X ac = *actual++;
- X if (isupper(ac))
- X ac = tolower(ac);
- X fc = *formal++;
- X switch (fc)
- X {
- X case 0:
- X result = !ac;
- X goto done;
- X
- X case '_':
- X if (ac == '-')
- X break;
- X /* fall through... */
- X
- X case 'a': case 'b': case 'c': case 'd': case 'e':
- X case 'f': case 'g': case 'h': case 'i': case 'j':
- X case 'k': case 'l': case 'm': case 'n': case 'o':
- X case 'p': case 'q': case 'r': case 's': case 't':
- X case 'u': case 'v': case 'w': case 'x': case 'y':
- X case 'z':
- X /*
- X * optional characters
- X */
- X if (ac == fc && arglex_compare(formal, actual))
- X {
- X result = 1;
- X goto done;
- X }
- X /*
- X * skip forward to next
- X * mandatory character, or after '_'
- X */
- X while (islower(*formal))
- X ++formal;
- X if (*formal == '_')
- X {
- X ++formal;
- X if (ac == '_' || ac == '-')
- X ++actual;
- X }
- X --actual;
- X break;
- X
- X case '*':
- X /*
- X * This is a hack, it should really
- X * check for a match match the stuff after
- X * the '*', too, a la glob.
- X */
- X if (!ac)
- X {
- X result = 0;
- X goto done;
- X }
- X partial = actual - 1;
- X result = 1;
- X goto done;
- X
- X case '\\':
- X if (actual[-1] != *formal++)
- X {
- X result = 0;
- X goto done;
- X }
- X break;
- X
- X case 'A': case 'B': case 'C': case 'D': case 'E':
- X case 'F': case 'G': case 'H': case 'I': case 'J':
- X case 'K': case 'L': case 'M': case 'N': case 'O':
- X case 'P': case 'Q': case 'R': case 'S': case 'T':
- X case 'U': case 'V': case 'W': case 'X': case 'Y':
- X case 'Z':
- X fc = tolower(fc);
- X /* fall through... */
- X
- X default:
- X /*
- X * mandatory characters
- X */
- X if (fc != ac)
- X {
- X result = 0;
- X goto done;
- X }
- X break;
- X }
- X }
- X done:
- X trace(("return %d;\n}\n", result));
- X return result;
- X}
- X
- X
- X/*
- X * NAME
- X * is_a_number
- X *
- X * SYNOPSIS
- X * int is_a_number(char *s);
- X *
- X * DESCRIPTION
- X * The is_a_number function is used to determine if the
- X * argument is a number.
- X *
- X * The value is placed in arglex_value.alv_number as
- X * a side effect.
- X *
- X * Negative and positive signs are accepted.
- X * The C conventions for decimal, octal and hexadecimal are understood.
- X *
- X * There may be no white space anywhere in the string,
- X * and the string must end after the last digit.
- X * Trailing garbage will be interpreted to mean it is not a string.
- X *
- X * ARGUMENTS
- X * s - string to be tested and evaluated
- X *
- X * RETURNS
- X * int; zero if not a number,
- X * non-zero if is a number.
- X */
- X
- Xstatic int is_a_number _((char *));
- X
- Xstatic int
- Xis_a_number(s)
- X char *s;
- X{
- X long n;
- X int sign;
- X
- X n = 0;
- X switch (*s)
- X {
- X case '-':
- X ++s;
- X sign = -1;
- X break;
- X
- X case '+':
- X ++s;
- X sign = 1;
- X break;
- X
- X default:
- X sign = 1;
- X break;
- X }
- X switch (*s)
- X {
- X case '0':
- X if ((s[1] == 'x' || s[1] == 'X') && s[2])
- X {
- X s += 2;
- X for (;;)
- X {
- X switch (*s)
- X {
- X case '0': case '1': case '2': case '3':
- X case '4': case '5': case '6': case '7':
- X case '8': case '9':
- X n = n * 16 + *s++ - '0';
- X continue;
- X
- X case 'A': case 'B': case 'C':
- X case 'D': case 'E': case 'F':
- X n = n * 16 + *s++ - 'A' + 10;
- X continue;
- X
- X case 'a': case 'b': case 'c':
- X case 'd': case 'e': case 'f':
- X n = n * 16 + *s++ - 'a' + 10;
- X continue;
- X }
- X break;
- X }
- X }
- X else
- X {
- X for (;;)
- X {
- X switch (*s)
- X {
- X case '0': case '1': case '2': case '3':
- X case '4': case '5': case '6': case '7':
- X n = n * 8 + *s++ - '0';
- X continue;
- X }
- X break;
- X }
- X }
- X break;
- X
- X case '1': case '2': case '3': case '4':
- X case '5': case '6': case '7': case '8': case '9':
- X for (;;)
- X {
- X switch (*s)
- X {
- X case '0': case '1': case '2': case '3':
- X case '4': case '5': case '6': case '7':
- X case '8': case '9':
- X n = n * 10 + *s++ - '0';
- X continue;
- X }
- X break;
- X }
- X break;
- X
- X default:
- X return 0;
- X }
- X if (*s)
- X return 0;
- X arglex_value.alv_number = n * sign;
- X return 1;
- X}
- X
- X
- X/*
- X * NAME
- X * arglex
- X *
- X * SYNOPSIS
- X * arglex_token_ty arglex(void);
- X *
- X * DESCRIPTION
- X * The arglex function is used to perfom lexical analysis
- X * on the command line arguments.
- X *
- X * Unrecognised options are returned as arglex_token_option
- X * for anything starting with a '-', or
- X * arglex_token_string otherwise.
- X *
- X * RETURNS
- X * The next token in the token stream.
- X * When the end is reached, arglex_token_eoln is returned forever.
- X *
- X * CAVEAT
- X * Must call arglex_init befor this function is called.
- X */
- X
- Xarglex_token_ty
- Xarglex()
- X{
- X arglex_table_ty *tp;
- X int j;
- X arglex_table_ty *hit[20];
- X int nhit;
- X char *arg;
- X static char *pushback[3];
- X static int pushback_depth;
- X
- X trace(("arglex()\n{\n"/*}*/));
- X if (pushback_depth)
- X {
- X /*
- X * the second half of a "-foo=bar" style argument.
- X */
- X arg = pushback[--pushback_depth];
- X }
- X else
- X {
- X if (argc <= 0)
- X {
- X arglex_token = arglex_token_eoln;
- X arg = "";
- X goto done;
- X }
- X arg = argv[0];
- X argc--;
- X argv++;
- X
- X /*
- X * See if it looks like a GNU "-foo=bar" option.
- X * Split it at the '=' to make it something the
- X * rest of the code understands.
- X */
- X if (arg[0] == '-' && arg[1] != '=')
- X {
- X char *eqp;
- X
- X eqp = strchr(arg, '=');
- X if (eqp)
- X {
- X pushback[pushback_depth++] = eqp + 1;
- X *eqp = 0;
- X }
- X }
- X
- X /*
- X * Turn the GNU-style leading "--"
- X * into "-" if necessary.
- X */
- X if
- X (
- X arg[0] == '-'
- X &&
- X arg[1] == '-'
- X &&
- X arg[2]
- X &&
- X !is_a_number(arg + 1)
- X )
- X ++arg;
- X }
- X
- X /*
- X * see if it is a number
- X */
- X if (is_a_number(arg))
- X {
- X arglex_token = arglex_token_number;
- X goto done;
- X }
- X
- X /*
- X * scan the tables to see what it matches
- X */
- X nhit = 0;
- X partial = 0;
- X for (tp = table; tp < ENDOF(table); tp++)
- X {
- X if (arglex_compare(tp->name, arg))
- X hit[nhit++] = tp;
- X }
- X if (utable)
- X {
- X for (tp = utable; tp->name; tp++)
- X {
- X if (arglex_compare(tp->name, arg))
- X hit[nhit++] = tp;
- X }
- X }
- X
- X /*
- X * deal with unknown or ambiguous options
- X */
- X switch (nhit)
- X {
- X case 0:
- X /*
- X * not found in the tables
- X */
- X if (*arg == '-')
- X arglex_token = arglex_token_option;
- X else
- X arglex_token = arglex_token_string;
- X break;
- X
- X case 1:
- X if (partial)
- X pushback[pushback_depth++] = partial;
- X arg = hit[0]->name;
- X arglex_token = hit[0]->token;
- X break;
- X
- X default:
- X {
- X string_ty *s1;
- X string_ty *s2;
- X
- X s1 = str_from_c(hit[0]->name);
- X for (j = 1; j < nhit; ++j)
- X {
- X s2 = str_format("%S, %s", s1, hit[j]->name);
- X str_free(s1);
- X s1 = s2;
- X }
- X fatal
- X (
- X "option \"%s\" ambiguous (%s)",
- X arg,
- X s1->str_text
- X );
- X }
- X }
- X
- X /*
- X * here for all exits
- X */
- X done:
- X arglex_value.alv_string = arg;
- X trace(("return %d; /* \"%s\" */\n", arglex_token, arg));
- X trace((/*{*/"}\n"));
- X return arglex_token;
- X}
- X
- X
- X/*
- X * NAME
- X * arglex_init_from_env - initialize analyzer
- X *
- X * SYNOPSIS
- X * void arglex_init_from_env(arglex_table_ty *);
- X *
- X * DESCRIPTION
- X * The arglex_init_from_env function is used to initialize the command
- X * line lexical analyzer form the COOK environment variable.
- X *
- X * RETURNS
- X * void
- X *
- X * CAVEAT
- X * Must be called befor the arglex function.
- X */
- X
- Xvoid
- Xarglex_init_from_env(av0, table)
- X char *av0;
- X arglex_table_ty *table;
- X{
- X char *cp1;
- X char *cp2;
- X size_t ac;
- X char **av;
- X
- X trace(("arglex_init_from_env()\n{\n"));
- X av0 = basename(av0);
- X cp1 = mem_alloc(strlen(av0) + 1);
- X strcpy(cp1, av0);
- X for (cp2 = cp1; *cp2; ++cp2)
- X {
- X if (islower(*cp2))
- X *cp2 = toupper(*cp2);
- X }
- X
- X ac = 0;
- X av = 0;
- X *(char **)enlarge(&ac, (char **)&av, sizeof(char *)) = av0;
- X
- X cp2 = getenv(cp1);
- X free(cp1);
- X if (!cp2)
- X goto done;
- X
- X /* make a copy so we don't damage the environment copy */
- X cp1 = mem_alloc(strlen(cp2) + 1);
- X strcpy(cp1, cp2);
- X
- X for (;;)
- X {
- X while (*cp1 == ' ')
- X ++cp1;
- X if (!*cp1)
- X break;
- X cp2 = cp1;
- X while (*++cp1 != ' ' && *cp1)
- X ;
- X if (*cp1)
- X *cp1++ = 0;
- X *(char **)enlarge(&ac, (char **)&av, sizeof(char *)) = cp2;
- X }
- X done:
- X arglex_init(ac, av, table);
- X trace((/*{*/"}\n"));
- X}
- X
- Xvoid
- Xarglex_set_progname(argv0)
- X char *argv0;
- X{
- X progname = basename(argv0);
- X}
- END_OF_FILE
- if test 11998 -ne `wc -c <'common/arglex.c'`; then
- echo shar: \"'common/arglex.c'\" unpacked with wrong size!
- fi
- # end of 'common/arglex.c'
- fi
- if test -f 'config' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'config'\"
- else
- echo shar: Extracting \"'config'\" \(11021 characters\)
- sed "s/^X//" >'config' <<'END_OF_FILE'
- X/*
- X * cook - file construction tool
- X * Copyright (C) 1991, 1992, 1993 Peter Miller.
- X * All rights reserved.
- X *
- X * This program is free software; you can redistribute it and/or modify
- X * it under the terms of the GNU General Public License as published by
- X * the Free Software Foundation; either version 2 of the License, or
- X * (at your option) any later version.
- X *
- X * This program is distributed in the hope that it will be useful,
- X * but WITHOUT ANY WARRANTY; without even the implied warranty of
- X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- X * GNU General Public License for more details.
- X *
- X * You should have received a copy of the GNU General Public License
- X * along with this program; if not, write to the Free Software
- X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- X *
- X * MANIFEST: instructions to aegis, per-project configuration
- X */
- X
- X/*
- X * how to build the project
- X * (actually, how to do an integration build)
- X * mandatory
- X */
- Xbuild_command =
- X "cook -b ${source aux/Howto.cook} project=$project \
- Xchange=$change version=$version -nl";
- X
- X/*
- X * When do -Integrate_Begin, link the baseline to the integration directory,
- X * rather than copying it.. This should be much faster.
- X *
- X * There is a gotcha: all the recipes in Howto.cook must unlink their targets
- X * before re-creating them, otherwise the baseline will be trashed.
- X */
- Xlink_integration_directory = true;
- X
- X/*
- X * create a new history
- X * always executed as the project owner
- X */
- Xhistory_create_command =
- X "fhist ${b $history} -create -i $input -p ${d $history} -r";
- X
- X/*
- X * get a file from history
- X * may be executed by developers
- X */
- Xhistory_get_command =
- X "fhist ${b $history} -e '$edit' -o $output -p ${d $history}";
- X
- X/*
- X * add a new change to the history
- X * always executed as the project owner
- X */
- Xhistory_put_command =
- X "fhist ${b $history} -u -i $input -p ${d $history} -r";
- X
- X/*
- X * query the topmost edit of a history file
- X * Result to be printed on stdout.
- X * may be executed by developers
- X */
- Xhistory_query_command =
- X "fhist ${b $history} -l 0 -p ${d $history} -q";
- X
- X/*
- X * difference of 2 files
- X */
- Xdiff_command =
- X "fcomp -w -s $original $input -o $output";
- X
- X/*
- X * difference of 3 files
- X */
- Xdiff3_command =
- X "fmerge $original $mostRecent $input -o $output";
- X
- X/*
- X * whenever files are added to or removed from the change,
- X * execute the following command.
- X */
- Xchange_file_command = "rm -f .c_inclrc";
- X
- X/*
- X * new file templates
- X */
- Xfile_template =
- X[
- X {
- X pattern = [ "*.[cyl]" ];
- X body ="\
- X/*\n\
- X * cook - file construction tool\n\
- X * Copyright (C) ${date %Y} Peter Miller.\n\
- X * All rights reserved.\n\
- X *\n\
- X * This program is free software; you can redistribute it and/or modify\n\
- X * it under the terms of the GNU General Public License as published by\n\
- X * the Free Software Foundation; either version 2 of the License, or\n\
- X * (at your option) any later version.\n\
- X *\n\
- X * This program is distributed in the hope that it will be useful,\n\
- X * but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
- X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
- X * GNU General Public License for more details.\n\
- X *\n\
- X * You should have received a copy of the GNU General Public License\n\
- X * along with this program; if not, write to the Free Software\n\
- X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\
- X *\n\
- X * MANIFEST: functions to manipulate ${basename $fn .c}s\n\
- X */\n";
- X },
- X {
- X pattern = [ "*.h" ];
- X body = "\
- X/*\n\
- X * cook - file construction tool\n\
- X * Copyright (C) ${date %Y} Peter Miller.\n\
- X * All rights reserved.\n\
- X *\n\
- X * This program is free software; you can redistribute it and/or modify\n\
- X * it under the terms of the GNU General Public License as published by\n\
- X * the Free Software Foundation; either version 2 of the License, or\n\
- X * (at your option) any later version.\n\
- X *\n\
- X * This program is distributed in the hope that it will be useful,\n\
- X * but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
- X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
- X * GNU General Public License for more details.\n\
- X *\n\
- X * You should have received a copy of the GNU General Public License\n\
- X * along with this program; if not, write to the Free Software\n\
- X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\
- X *\n\
- X * MANIFEST: interface definition for ${basename $fn .c}\n\
- X */\n\
- X\n\
- X#ifndef ${upcase ${id $fn}}\n\
- X#define ${upcase ${id $fn}}\n\
- X\n\
- X#include <main.h>\n\
- X\n\
- X#endif /* ${upcase ${id $fn}} */\n";
- X },
- X {
- X pattern = [ "test/*/*.sh" ];
- X body = "\
- X#! /bin/sh\n\
- X#\n\
- X# cook - file construction tool\n\
- X# Copyright (C) ${date %Y} Peter Miller.\n\
- X# All rights reserved.\n\
- X#\n\
- X# This program is free software; you can redistribute it and/or modify\n\
- X# it under the terms of the GNU General Public License as published by\n\
- X# the Free Software Foundation; either version 2 of the License, or\n\
- X# (at your option) any later version.\n\
- X#\n\
- X# This program is distributed in the hope that it will be useful,\n\
- X# but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
- X# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
- X# GNU General Public License for more details.\n\
- X#\n\
- X# You should have received a copy of the GNU General Public License\n\
- X# along with this program; if not, write to the Free Software\n\
- X# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\
- X#\n\
- X# MANIFEST: Test the ??? functionality\n\
- X#\n\
- X\n\
- Xwork=/tmp/$$$$\n\
- XPAGER=cat\n\
- Xexport PAGER\n\
- X\n\
- Xhere=`pwd`\n\
- Xif test $$? -ne 0 ; then exit 1; fi\n\
- X\n\
- Xfail()\n\
- X{\n\
- X set +x\n\
- X echo 'FAILED test of the ??? functionality' 1>&2\n\
- X cd $$here\n\
- X find $$work -type d -user $$USER -exec chmod u+w {} \\;\n\
- X rm -rf $$work\n\
- X exit 1\n\
- X}\n\
- Xpass()\n\
- X{\n\
- X set +x\n\
- X echo PASSED 1>&2\n\
- X cd $$here\n\
- X find $$work -type d -user $$USER -exec chmod u+w {} \\;\n\
- X rm -rf $$work\n\
- X exit 0\n\
- X}\n\
- Xtrap \"fail\" 1 2 3 15\n\
- X\n\
- Xmkdir $$work\n\
- Xif test $$? -ne 0 ; then exit 1; fi\n\
- Xcd $$work\n\
- Xif test $$? -ne 0 ; then fail; fi\n\
- X\n\
- X#\n\
- X# put your test here\n\
- X#\n\
- X$$here/myprog\n\
- Xif test $$? -ne 0 ; then fail; fi\n\
- X\n\
- X#\n\
- X# Only definite negatives are possible.\n\
- X# The functionality exercised by this test appears to work,\n\
- X# no other guarantees are made.\n\
- X#\n\
- Xpass\n";
- X },
- X {
- X pattern = [ "*.sh" ];
- X body = "\
- X#! /bin/sh\n\
- X#\n\
- X# cook - file construction tool\n\
- X# Copyright (C) ${date %Y} Peter Miller.\n\
- X# All rights reserved.\n\
- X#\n\
- X# This program is free software; you can redistribute it and/or modify\n\
- X# it under the terms of the GNU General Public License as published by\n\
- X# the Free Software Foundation; either version 2 of the License, or\n\
- X# (at your option) any later version.\n\
- X#\n\
- X# This program is distributed in the hope that it will be useful,\n\
- X# but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
- X# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
- X# GNU General Public License for more details.\n\
- X#\n\
- X# You should have received a copy of the GNU General Public License\n\
- X# along with this program; if not, write to the Free Software\n\
- X# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\
- X#\n\
- X# MANIFEST: shell script to ${basename $fn .sh}\n\
- X#\n\
- X\n\
- Xexit 0\n";
- X },
- X {
- X pattern = [ "*.man", "*.[12345678]" ];
- X body = "\
- X'\\\" t\n\
- X.\\\" cook - file construction tool\n\
- X.\\\" Copyright (C) ${date %Y} Peter Miller.\n\
- X.\\\" All rights reserved.\n\
- X.\\\"\n\
- X.\\\" This program is free software; you can redistribute it and/or modify\n\
- X.\\\" it under the terms of the GNU General Public License as published by\n\
- X.\\\" the Free Software Foundation; either version 2 of the License, or\n\
- X.\\\" (at your option) any later version.\n\
- X.\\\"\n\
- X.\\\" This program is distributed in the hope that it will be useful,\n\
- X.\\\" but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
- X.\\\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
- X.\\\" GNU General Public License for more details.\n\
- X.\\\"\n\
- X.\\\" You should have received a copy of the GNU General Public License\n\
- X.\\\" along with this program; if not, write to the Free Software\n\
- X.\\\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\
- X.\\\"\n\
- X.\\\" MANIFEST: manual entry for the ${basename $fn .1} command\n\
- X.\\\"\n\
- X.TH cook 1\n\
- X.ds n) cook\n\
- X.if n .ad l\n\
- X.if n .hy 0\n\
- X.if n .nr IN 8n\n\
- X.SH NAME\n\
- X\\*(n) \\- file construction tool\n\
- X.SH SYNOPSIS\n\
- X.B \\*(n)\n\
- X[\n\
- X.IR option \\&...\n\
- X]\n\
- X.IR filename \\&...\n\
- X.br\n\
- X.B \\*(n)\n\
- X.B -Help\n\
- X.br\n\
- X.B \\*(n)\n\
- X.B -VERSion\n\
- X.SH DESCRIPTION\n\
- XThe\n\
- X.I \\*(n)\n\
- Xprogram is used to\n\
- X.br\n\
- X.ne 1i\n\
- X.SH OPTIONS\n\
- XThe following options are understood:\n\
- X.TP 8n\n\
- X.B -Help\n\
- X.br\n\
- XProvide some help with using the\n\
- X.I \\*(n)\n\
- Xprogram.\n\
- X.TP 8n\n\
- X.B -VERSion\n\
- X.br\n\
- XPrint the version of the\n\
- X.I \\*(n)\n\
- Xprogram being executed.\n\
- X.PP\n\
- XAll other options will produce a diagnostic error.\n\
- X.so o__rules.so\n\
- X.so copyright.so\n";
- X },
- X {
- X pattern = [ "*.so", "*.ms", "*.me" ];
- X body = "\
- X.\\\"\n\
- X.\\\" cook - file construction tool\n\
- X.\\\" Copyright (C) ${date %Y} Peter Miller.\n\
- X.\\\" All rights reserved.\n\
- X.\\\"\n\
- X.\\\" This program is free software; you can redistribute it and/or modify\n\
- X.\\\" it under the terms of the GNU General Public License as published by\n\
- X.\\\" the Free Software Foundation; either version 2 of the License, or\n\
- X.\\\" (at your option) any later version.\n\
- X.\\\"\n\
- X.\\\" This program is distributed in the hope that it will be useful,\n\
- X.\\\" but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
- X.\\\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
- X.\\\" GNU General Public License for more details.\n\
- X.\\\"\n\
- X.\\\" You should have received a copy of the GNU General Public License\n\
- X.\\\" along with this program; if not, write to the Free Software\n\
- X.\\\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\
- X.\\\"\n\
- X.\\\" MANIFEST: document describing ${basename $fn}\n\
- X.\\\"\n";
- X },
- X {
- X pattern = [ "*" ];
- X body = "\
- X#\n\
- X# cook - file construction tool\n\
- X# Copyright (C) ${date %Y} Peter Miller.\n\
- X# All rights reserved.\n\
- X#\n\
- X# This program is free software; you can redistribute it and/or modify\n\
- X# it under the terms of the GNU General Public License as published by\n\
- X# the Free Software Foundation; either version 2 of the License, or\n\
- X# (at your option) any later version.\n\
- X#\n\
- X# This program is distributed in the hope that it will be useful,\n\
- X# but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
- X# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
- X# GNU General Public License for more details.\n\
- X#\n\
- X# You should have received a copy of the GNU General Public License\n\
- X# along with this program; if not, write to the Free Software\n\
- X# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\
- X#\n\
- X# MANIFEST: fill me in\n\
- X#\n";
- X }
- X];
- END_OF_FILE
- if test 11021 -ne `wc -c <'config'`; then
- echo shar: \"'config'\" unpacked with wrong size!
- fi
- # end of 'config'
- fi
- if test -f 'cook/expr.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'cook/expr.c'\"
- else
- echo shar: Extracting \"'cook/expr.c'\" \(12316 characters\)
- sed "s/^X//" >'cook/expr.c' <<'END_OF_FILE'
- X/*
- X * cook - file construction tool
- X * Copyright (C) 1990, 1991, 1992, 1993 Peter Miller.
- X * All rights reserved.
- X *
- X * This program is free software; you can redistribute it and/or modify
- X * it under the terms of the GNU General Public License as published by
- X * the Free Software Foundation; either version 2 of the License, or
- X * (at your option) any later version.
- X *
- X * This program is distributed in the hope that it will be useful,
- X * but WITHOUT ANY WARRANTY; without even the implied warranty of
- X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- X * GNU General Public License for more details.
- X *
- X * You should have received a copy of the GNU General Public License
- X * along with this program; if not, write to the Free Software
- X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- X *
- X * MANIFEST: functions to manipulate expression trees
- X *
- X * This file contains the functions for manipulating expression
- X * trees; building, interpreting and freeing them.
- X */
- X
- X#include <stddef.h>
- X#include <stdio.h>
- X
- X#include <builtin.h>
- X#include <conf.h>
- X#include <cook.h>
- X#include <error.h>
- X#include <expr.h>
- X#include <id.h>
- X#include <lex.h>
- X#include <match.h>
- X#include <mem.h>
- X#include <option.h>
- X#include <stmt.h>
- X#include <s-v-arg.h>
- X#include <trace.h>
- X#include <word.h>
- X
- X
- Xstatic position *expr_context;
- X
- X
- X/*
- X * NAME
- X * expr_alloc - allocate a pointer structure
- X *
- X * SYNOPSIS
- X * expr *expr_alloc(void);
- X *
- X * DESCRIPTION
- X * The expr_alloc function is used to allocate an expression node
- X * structure in dynamic memory. It will initially be filled with zeros.
- X * The e_op field is defined as being non-zero to allow detection
- X * of some classes of missuse of the expression nodes.
- X *
- X * RETURNS
- X * A pointer to a "expr" in dynamic memory.
- X *
- X * CAVEAT
- X * The expression node is allocated in dynamic memory,
- X * it is the callers responsibility to ensure that it is freed
- X * when it is finished with, by a call to expr_free().
- X */
- X
- Xexpr *
- Xexpr_alloc()
- X{
- X expr *ep;
- X
- X trace(("expr_alloc()\n{\n"/*}*/));
- X ep = (expr *)mem_alloc_clear(sizeof(expr));
- X ep->e_references = 1;
- X ep->e_position.pos_name = str_copy(lex_cur_file());
- X ep->e_position.pos_line = lex_cur_line();
- X trace(("return %08lX;\n", ep));
- X trace((/*{*/"}\n"));
- X return ep;
- X}
- X
- X
- X/*
- X * NAME
- X * expr_copy - copy and expression
- X *
- X * SYNOPSIS
- X * expr *expr_copy(expr *);
- X *
- X * DESCRIPTION
- X * The expr_copy function is used to make a copy of an expression tree.
- X *
- X * RETURNS
- X * The expr_copy function returns a pointer to the root of the copied
- X * expression tree.
- X *
- X * CAVEAT
- X * The result is in dynamic memory, used expr_free to dispose of it when
- X * finished with.
- X */
- X
- Xexpr *
- Xexpr_copy(ep)
- X expr *ep;
- X{
- X trace(("expr_copy(ep = %08X)\n{\n"/*}*/, ep));
- X ep->e_references++;
- X trace(("return %08lX;\n", ep));
- X trace((/*{*/"}\n"));
- X return ep;
- X}
- X
- X
- X/*
- X * NAME
- X * expr_free - free expression tree
- X *
- X * SYNOPSIS
- X * void expr_free(expr *ep);
- X *
- X * DESCRIPTION
- X * The expr_free function is used to free expression trees.
- X *
- X * CAVEAT
- X * It is assumed that the expression trees are all
- X * dynamically allocated. Use expr_alloc() to allocate them.
- X */
- X
- Xvoid
- Xexpr_free(ep)
- X expr *ep;
- X{
- X trace(("expr_free(ep = %08X)\n{\n"/*}*/, ep));
- X assert(ep);
- X ep->e_references--;
- X if (ep->e_references > 0)
- X goto ret;
- X str_free(ep->e_position.pos_name);
- X switch ((int)ep->e_op)
- X {
- X default:
- X fatal("illegal expression opcode %d (bug)", ep->e_op);
- X
- X case OP_CAT:
- X expr_free(ep->e_left);
- X expr_free(ep->e_right);
- X break;
- X
- X case OP_FUNC:
- X el_free(&ep->e_list);
- X break;
- X
- X case OP_WORD:
- X str_free(ep->e_word);
- X break;
- X }
- X mem_free((char *)ep);
- Xret:
- X trace((/*{*/"}\n"));
- X}
- X
- X
- X/*
- X * NAME
- X * el_free - free expression lists
- X *
- X * SYNOPSIS
- X * void el_free(elist *elp);
- X *
- X * DESCRIPTION
- X * The el_free function is used to free expression lists,
- X * it calls expr_free for each expression in the list.
- X *
- X * CAVEAT
- X * It is assumed that the expressions are dynamically allocated,
- X * and that the expression list was grown using el_append().
- X * The actual structure pointed to is NOT assumed to be in dynamic memory
- X * and should not be passed to free().
- X */
- X
- Xvoid
- Xel_free(elp)
- X elist *elp;
- X{
- X int j;
- X
- X trace(("el_free(elp = %08X)\n{\n"/*}*/, elp));
- X for (j = 0; j < elp->el_nexprs; j++)
- X expr_free(elp->el_expr[j]);
- X if (elp->el_nexprs)
- X mem_free((char *)elp->el_expr);
- X elp->el_nexprs = 0;
- X elp->el_expr = 0;
- X trace((/*{*/"}\n"));
- X}
- X
- X
- X/*
- X * NAME
- X * el_copy - copy expression list
- X *
- X * SYNOPSIS
- X * void el_copy(elist *to, elist *from);
- X *
- X * DESCRIPTION
- X * The el_copy function is used to copy the list of expression trees
- X * pointed to by `from' into the expression list pointed to by `to'.
- X *
- X * RETURNS
- X * void
- X *
- X * CAVEAT
- X * The el_free function must be used to dispose of the list when
- X * finished with.
- X */
- X
- Xvoid
- Xel_copy(to, from)
- X elist *to;
- X elist *from;
- X{
- X int j;
- X
- X trace(("el_copy(to = %08X, from = %08X)\n{\n"/*}*/, to, from));
- X el_zero(to);
- X if (!from->el_nexprs)
- X return;
- X for (j = 0; j < from->el_nexprs; j++)
- X el_append(to, from->el_expr[j]);
- X trace((/*{*/"}\n"));
- X}
- X
- X
- X/*
- X * NAME
- X * expr_eval - evaluate an expression
- X *
- X * SYNOPSIS
- X * int expr_eval(wlist *result, expr *ep);
- X *
- X * DESCRIPTION
- X * The expr_eval function is used to evaluate an expression.
- X *
- X * RETURNS
- X * The results of the expression evaluation are appended to
- X * the word list pointed to by 'results' argument using wl_append.
- X *
- X * The functiuon result is -1 for evaluation errors, 0 for no error.
- X *
- X * CAVEAT
- X * It is assumed that the results wordlist has been initialised
- X * before it was passed to expr_eval().
- X *
- X * The result returned from this function are allocated in dynamic memory.
- X * It is the responsibility of the caller to ensure that they are freed
- X * when they are finished with, using wl_free().
- X */
- X
- Xint
- Xexpr_eval(result, ep)
- X wlist *result;
- X expr *ep;
- X{
- X int retval;
- X
- X trace(("expr_eval(result = %08X, ep = %08X)\n{\n"/*}*/, result, ep));
- X assert(ep);
- X switch ((int)ep->e_op)
- X {
- X default:
- X error
- X (
- X "%s: %d: illegal expression opcode %d (bug)",
- X ep->e_position.pos_name->str_text,
- X ep->e_position.pos_line,
- X ep->e_op
- X );
- X expr_eval_fails:
- X option_set_errors();
- X retval = -1;
- X goto ret;
- X
- X case OP_WORD:
- X {
- X match_ty *field;
- X
- X /*
- X * If a wildcard mapping is in force (we are performing
- X * actions bound to an implicit recipe) the word will be
- X * mapped before it is returned.
- X */
- X field = match_top();
- X if (field)
- X {
- X string_ty *s;
- X
- X s = reconstruct(ep->e_word, field);
- X wl_append(result, s);
- X str_free(s);
- X }
- X else
- X wl_append(result, ep->e_word);
- X }
- X break;
- X
- X case OP_FUNC:
- X {
- X wlist wl;
- X
- X if (el2wl(&wl, &ep->e_list))
- X {
- X wl_free(&wl);
- X goto expr_eval_fails;
- X }
- X switch (wl.wl_nwords)
- X {
- X case 0:
- X break;
- X
- X case 1:
- X {
- X wlist value;
- X
- X if (id_search(wl.wl_word[0], ID_CLASS_VARIABLE, &value))
- X {
- X int j;
- X
- X for (j = 0; j < value.wl_nwords; j++)
- X wl_append(result, value.wl_word[j]);
- X wl_free(&value);
- X break;
- X }
- X
- X /*
- X * If the variable is not found,
- X * fall through into the function case
- X */
- X }
- X
- X default:
- X {
- X bifp code;
- X
- X if (!id_search(wl.wl_word[0], ID_CLASS_BUILTIN, &code))
- X {
- X error
- X (
- X "%s: %d: undefined %s \"%s\"",
- X ep->e_position.pos_name->str_text,
- X ep->e_position.pos_line,
- X wl.wl_nwords >= 2 ? "function" : "variable",
- X wl.wl_word[0]->str_text
- X );
- X goto expr_eval_fails;
- X }
- X expr_context = &ep->e_position;
- X if ((*code)(result, &wl))
- X goto expr_eval_fails;
- X }
- X break;
- X }
- X wl_free(&wl);
- X }
- X break;
- X
- X case OP_CAT:
- X {
- X wlist left;
- X wlist right;
- X int j;
- X
- X /*
- X * Form the two word lists.
- X * Tack the last word of the left list
- X * onto the first word of the right list.
- X *
- X * There are other conceivable ways to do this,
- X * but this definition gives the fewest surprises.
- X */
- X wl_zero(&left);
- X if (expr_eval(&left, ep->e_left))
- X {
- X wl_free(&left);
- X goto expr_eval_fails;
- X }
- X wl_zero(&right);
- X if (expr_eval(&right, ep->e_right))
- X {
- X wl_free(&left);
- X wl_free(&right);
- X goto expr_eval_fails;
- X }
- X switch ((left.wl_nwords ? 1 : 0) | (right.wl_nwords ? 2 : 0))
- X {
- X case 0:
- X /* both lists empty */
- X break;
- X
- X case 1:
- X /* right list empty */
- X for (j = 0; j < left.wl_nwords; j++)
- X wl_append(result, left.wl_word[j]);
- X break;
- X
- X case 2:
- X /* left list empty */
- X for (j = 0; j < right.wl_nwords; j++)
- X wl_append(result, right.wl_word[j]);
- X break;
- X
- X case 3:
- X {
- X string_ty *s;
- X
- X /* at least one word in each list */
- X for (j = 0; j < left.wl_nwords - 1; j++)
- X wl_append(result, left.wl_word[j]);
- X s = str_catenate(left.wl_word[j], right.wl_word[0]);
- X wl_append(result, s);
- X str_free(s);
- X for (j = 1; j < right.wl_nwords; j++)
- X wl_append(result, right.wl_word[j]);
- X }
- X break;
- X }
- X wl_free(&left);
- X wl_free(&right);
- X }
- X break;
- X }
- X retval = 0;
- Xret:
- X trace(("return %d;\n", retval));
- X trace((/*{*/"}\n"));
- X return retval;
- X}
- X
- X
- X/*
- X * NAME
- X * el_append - append to an expression list
- X *
- X * SYNOPSIS
- X * void el_append(elist *el, expr *e);
- X *
- X * DESCRIPTION
- X * The el_append function is used to append an expression to an expression
- X * list.
- X *
- X * RETURNS
- X * void
- X *
- X * CAVEAT
- X * The expression has not been copied, so do not hand it
- X * to expr_free after you append it.
- X *
- X * It is assumed that the elist has been previously initialised by a
- X * elist el;
- X * el_zero(&el);
- X * statement (or similar) before this function is called.
- X */
- X
- Xvoid
- Xel_append(el, e)
- X elist *el;
- X expr *e;
- X{
- X trace(("el_append(el = %08X, e = %08X)\n{\n"/*}*/, el, e));
- X assert(el);
- X assert(e);
- X assert(!el->el_nexprs || !!el->el_expr);
- X *(expr **)enlarge(&el->el_nexprs, (char**)&el->el_expr, sizeof(expr *)) = expr_copy(e);
- X trace((/*{*/"}\n"));
- X}
- X
- X
- X/*
- X * NAME
- X * el2wl - expression list to word list
- X *
- X * SYNOPSIS
- X * int el2wl(wlist *wl, elist *el);
- X *
- X * DESCRIPTION
- X * The el2wl function is used to turn an expression list into a word list.
- X *
- X * RETURNS
- X * The word list is initialised before it is used to
- X * store the results of evaluating the expressions.
- X *
- X * The function return value is -1 for errors and 0 for success.
- X *
- X * CAVEAT
- X * The results returned by this function are allocated in dynamic memory.
- X * It is the responsibility of the caller to free them when finished with,
- X * by a call to wl_free().
- X */
- X
- Xint
- Xel2wl(wlp, elp)
- X wlist *wlp;
- X elist *elp;
- X{
- X int j;
- X int retval;
- X
- X trace(("el2wl(wlp = %08X, elp = %08X)\n{\n"/*}*/, wlp, elp));
- X retval = 0;
- X wl_zero(wlp);
- X for (j = 0; j < elp->el_nexprs; j++)
- X {
- X if (expr_eval(wlp, elp->el_expr[j]))
- X {
- X retval = -1;
- X break;
- X }
- X }
- X trace(("return %d;\n", retval));
- X trace((/*{*/"}\n"));
- X return retval;
- X}
- X
- X
- X/*
- X * NAME
- X * expr_eveal_condition - evaluate condition
- X *
- X * SYNOPSIS
- X * int expr_eval_condition(expr *);
- X *
- X * DESCRIPTION
- X * The expr_eval_condition function is used to evaluate an expression to
- X * yeild a true/false result. The expression is evaluated into a word
- X * list. A false result is if all of the resulting strings are empty or
- X * 0, true otherwise.
- X *
- X * RETURNS
- X * The expr_eval_condition function returns 0 if the condition is false,
- X * and nonzero if it is true. The value -1 is returned on error.
- X *
- X * CAVEAT
- X * The str_bool function is used to test the booean value of a string;
- X * changeing the behaviour of that function will change the behaviour of
- X * this one.
- X */
- X
- Xint
- Xexpr_eval_condition(ep)
- X expr *ep;
- X{
- X wlist wl;
- X int j;
- X int result;
- X
- X trace(("expr_eval_condition(ep = %08X)\n{\n"/*}*/, ep));
- X assert(ep);
- X wl_zero(&wl);
- X if (expr_eval(&wl, ep))
- X {
- X result = -1;
- X goto ret;
- X }
- X for (j = 0; j < wl.wl_nwords; ++j)
- X {
- X if (str_bool(wl.wl_word[j]))
- X {
- X wl_free(&wl);
- X result = 1;
- X goto ret;
- X }
- X }
- X wl_free(&wl);
- X result = 0;
- Xret:
- X trace(("return %d;\n", result));
- X trace((/*{*/"}\n"));
- X return result;
- X}
- X
- X
- X/*VARARGS1*/
- Xvoid
- Xexpr_error(s sva_last)
- X char *s;
- X sva_last_decl
- X{
- X va_list ap;
- X char buffer[1 << 11];
- X
- X sva_init(ap, s);
- X vsprintf(buffer, s, ap);
- X va_end(ap);
- X error
- X (
- X "%s: %d: %s",
- X expr_context->pos_name->str_text,
- X expr_context->pos_line,
- X buffer
- X );
- X}
- X
- X
- Xvoid
- Xel_zero(elp)
- X elist *elp;
- X{
- X elp->el_nexprs = 0;
- X elp->el_expr = 0;
- X}
- END_OF_FILE
- if test 12316 -ne `wc -c <'cook/expr.c'`; then
- echo shar: \"'cook/expr.c'\" unpacked with wrong size!
- fi
- # end of 'cook/expr.c'
- fi
- if test -f 'cook/hashline.y' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'cook/hashline.y'\"
- else
- echo shar: Extracting \"'cook/hashline.y'\" \(12945 characters\)
- sed "s/^X//" >'cook/hashline.y' <<'END_OF_FILE'
- X/*
- X * cook - file construction tool
- X * Copyright (C) 1991, 1992, 1993 Peter Miller.
- X * All rights reserved.
- X *
- X * This program is free software; you can redistribute it and/or modify
- X * it under the terms of the GNU General Public License as published by
- X * the Free Software Foundation; either version 2 of the License, or
- X * (at your option) any later version.
- X *
- X * This program is distributed in the hope that it will be useful,
- X * but WITHOUT ANY WARRANTY; without even the implied warranty of
- X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- X * GNU General Public License for more details.
- X *
- X * You should have received a copy of the GNU General Public License
- X * along with this program; if not, write to the Free Software
- X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- X *
- X * MANIFEST: functions to parse #directive lines in cookbooks
- X #
- X * The hashline.y and parse.y parsers share the same lexer.
- X * This means using the classic sed hack for yacc output.
- X * Note that the expression grammars must be as similar as possible
- X * in the two grammars.
- X *
- X * The state table in the condition frames is very simple:
- X * state 0: before #if
- X * state 1: after #if (and variants)
- X * state 2: after #elif
- X * state 3: after #else
- X * state 0: after #endif
- X */
- X
- X%{
- X
- X#include <stddef.h>
- X#include <string.h>
- X#include <time.h>
- X#include <stdlib.h>
- X
- X#include <error.h>
- X#include <expr.h>
- X#include <hashline.h>
- X#include <lex.h>
- X#include <mem.h>
- X#include <option.h>
- X#include <os.h>
- X#include <trace.h>
- X#include <word.h>
- X
- X
- Xstatic wlist done_once;
- X
- X
- Xtypedef struct cond cond;
- Xstruct cond
- X{
- X int pass;
- X int state;
- X cond *next;
- X};
- X
- Xstatic cond *stack;
- Xstatic cond *cond_free_list;
- X
- X#ifdef DEBUG
- X#define YYDEBUG 1
- X#define printf trace_where(__FILE__, __LINE__), lex_trace
- Xextern int yydebug;
- X#endif
- X
- X
- X#define yyerror lex_error
- X
- X
- X/*
- X * NAME
- X * open_include - open an include file
- X *
- X * SYNOPSIS
- X * void open_include(string_ty *filename);
- X *
- X * DESCRIPTION
- X * The open_include function is used to search for a given file name in
- X * the include path and lex_open it when found.
- X *
- X * RETURNS
- X * void
- X */
- X
- Xstatic void open_include_once _((string_ty *));
- X
- Xstatic void
- Xopen_include_once(filename)
- X string_ty *filename;
- X{
- X if (!wl_member(&done_once, filename))
- X lex_open(filename);
- X}
- X
- X
- Xstatic void open_include _((string_ty *, int));
- X
- Xstatic void
- Xopen_include(filename, local)
- X string_ty *filename;
- X int local;
- X{
- X int j;
- X string_ty *path;
- X
- X trace(("open_include(filename = %08lX, local = %d) entry",
- X filename, local));
- X trace_string(filename->str_text);
- X if (filename->str_text[0] != '/')
- X {
- X if (local)
- X {
- X string_ty *s;
- X
- X s = lex_cur_file();
- X if (strchr(s->str_text, '/'))
- X {
- X s = os_dirname(s);
- X if (!s)
- X {
- X bomb:
- X yyerror("unable to construct include file name");
- X goto ret;
- X }
- X path = str_format("%S/%S", s, filename);
- X str_free(s);
- X }
- X else
- X path = str_copy(filename);
- X switch (os_exists(path))
- X {
- X case -1:
- X str_free(path);
- X goto bomb;
- X
- X case 1:
- X open_include_once(path);
- X str_free(path);
- X goto ret;
- X }
- X str_free(path);
- X }
- X for (j = 0; j < option.o_search_path.wl_nwords; ++j)
- X {
- X path = str_format("%S/%S", option.o_search_path.wl_word[j], filename);
- X switch (os_exists(path))
- X {
- X case -1:
- X str_free(path);
- X goto bomb;
- X
- X case 1:
- X open_include_once(path);
- X str_free(path);
- X goto ret;
- X }
- X str_free(path);
- X }
- X }
- X open_include_once(filename);
- X ret:
- X trace((/*{*/"}\n"));
- X}
- X
- X
- X/*
- X * NAME
- X * hashline - the # control line processor
- X *
- X * SYNOPSIS
- X * void hashline(void);
- X *
- X * DESCRIPTION
- X * The hashline function is used to process # control lines.
- X *
- X * RETURNS
- X * void
- X */
- X
- Xvoid
- Xhashline()
- X{
- X int yyparse _((void)); /* forward */
- X
- X trace(("hashline()\n{\n"/*}*/));
- X#if YYDEBUG
- X yydebug = trace_pretest_;
- X#endif
- X yyparse();
- X trace((/*{*/"}\n"));
- X}
- X
- X
- X/*
- X * NAME
- X * cond_alloc - allocate a condition structure
- X *
- X * SYNOPSIS
- X * cond *cond_alloc(void);
- X *
- X * DESCRIPTION
- X * The cond_alloc function is used to allocate a condition structure
- X * from dynamic memory.
- X *
- X * RETURNS
- X * cond * - pointer to condition structure.
- X *
- X * CAVEAT
- X * A free list is maintained to avoid malloc overheads.
- X */
- X
- Xstatic cond *cond_alloc _((void));
- X
- Xstatic cond *
- Xcond_alloc()
- X{
- X cond *c;
- X
- X if (cond_free_list)
- X {
- X c = cond_free_list;
- X cond_free_list = c->next;
- X }
- X else
- X c = (cond *)mem_alloc(sizeof(cond));
- X return c;
- X}
- X
- X
- X/*
- X * NAME
- X * cond_free - free condition structure
- X *
- X * SYNOPSIS
- X * void cond_free(cond*);
- X *
- X * DESCRIPTION
- X * The cond_free function is used to indicate that a condition structure
- X * is finished with.
- X *
- X * RETURNS
- X * void
- X *
- X * CAVEAT
- X * A free list is maintained to avoid malloc overheads.
- X */
- X
- Xstatic void cond_free _((cond *));
- X
- Xstatic void
- Xcond_free(c)
- X cond *c;
- X{
- X c->next = cond_free_list;
- X cond_free_list = c;
- X}
- X
- X
- X/*
- X * NAME
- X * hash_include - process #include directive
- X *
- X * SYNOPSIS
- X * void hash_include(expr *filename);
- X *
- X * DESCRIPTION
- X * The hash_include function is used to process #include directives.
- X *
- X * RETURNS
- X * void
- X */
- X
- Xstatic void hash_include _((expr *));
- X
- Xstatic void
- Xhash_include(ep)
- X expr *ep;
- X{
- X wlist result;
- X string_ty *s;
- X
- X if (stack && !stack->pass)
- X return;
- X wl_zero(&result);
- X if (expr_eval(&result, ep))
- X {
- X wl_free(&result);
- X hashline_error("include file name evaluation failed");
- X return;
- X }
- X switch (result.wl_nwords)
- X {
- X case 0:
- X yyerror("expression produces no file name to include");
- X break;
- X
- X case 1:
- X s = result.wl_word[0];
- X if
- X (
- X s->str_length > 2
- X &&
- X s->str_text[0] == '<'
- X &&
- X s->str_text[s->str_length - 1] == '>'
- X )
- X {
- X s = str_n_from_c(s->str_text + 1, s->str_length - 2);
- X open_include(s, 0);
- X str_free(s);
- X }
- X else
- X {
- X if (s->str_length)
- X open_include(s, 1);
- X else
- X yyerror("expression produces null file name to include");
- X }
- X break;
- X
- X default:
- X yyerror("expression produces more than one file name to include");
- X break;
- X }
- X wl_free(&result);
- X}
- X
- X
- X/*
- X * NAME
- X * hash_if - process #if directive
- X *
- X * SYNOPSIS
- X * void hash_if(expr *);
- X *
- X * DESCRIPTION
- X * The hash_if function is used to process #if directives.
- X *
- X * RETURNS
- X * void
- X */
- X
- Xstatic void hash_if _((expr *));
- X
- Xstatic void
- Xhash_if(ep)
- X expr *ep;
- X{
- X cond *c;
- X
- X trace(("hash_if(ep = %08lX)\n{\n"/*}*/, ep));
- X c = cond_alloc();
- X c->next = stack;
- X if (stack && !stack->pass)
- X {
- X c->pass = 0;
- X c->state = 1;
- X lex_passing(0);
- X }
- X else
- X {
- X switch (expr_eval_condition(ep))
- X {
- X case -1:
- X yyerror("condition evaluation failed");
- X /* fall through... */
- X
- X case 0:
- X c->pass = 0;
- X c->state = 2;
- X lex_passing(0);
- X break;
- X
- X default:
- X c->pass = 1;
- X c->state = 1;
- X lex_passing(1);
- X break;
- X }
- X }
- X stack = c;
- X trace((/*{*/"}\n"));
- X}
- X
- X
- X/*
- X * NAME
- X * hash_ifdef - process #ifdef directive
- X *
- X * SYNOPSIS
- X * void hash_ifdef(expr*);
- X *
- X * DESCRIPTION
- X * The hash_ifdef function is used to process #ifdef directives.
- X *
- X * RETURNS
- X * void
- X */
- X
- Xstatic void hash_ifdef _((expr *));
- X
- Xstatic void
- Xhash_ifdef(ep)
- X expr *ep;
- X{
- X expr *e1;
- X expr *e2;
- X
- X trace(("hash_ifdef(ep = %08lX)\n{\n"/*}*/, ep));
- X e1 = expr_alloc();
- X e1->e_op = OP_WORD;
- X e1->e_word = str_from_c("defined");
- X e2 = expr_alloc();
- X e2->e_op = OP_FUNC;
- X el_append(&e2->e_list, e1);
- X el_append(&e2->e_list, ep);
- X expr_free(e1);
- X hash_if(e2);
- X expr_free(e2);
- X trace((/*{*/"}\n"));
- X}
- X
- X
- X/*
- X * NAME
- X * hash_ifndef - process #ifndef directives
- X *
- X * SYNOPSIS
- X * void hash_ifndef(expr *);
- X *
- X * DESCRIPTION
- X * The hash_ifndef function is used to process #ifndef directives.
- X *
- X * RETURNS
- X * void
- X */
- X
- Xstatic void hash_ifndef _((expr *));
- X
- Xstatic void
- Xhash_ifndef(ep)
- X expr *ep;
- X{
- X expr *e1;
- X expr *e2;
- X expr *e3;
- X
- X trace(("hash_ifndef(ep = %08lX)\n{\n"/*}*/, ep));
- X e1 = expr_alloc();
- X e1->e_op = OP_WORD;
- X e1->e_word = str_from_c("defined");
- X e2 = expr_alloc();
- X e2->e_op = OP_FUNC;
- X el_append(&e2->e_list, e1);
- X el_append(&e2->e_list, ep);
- X expr_free(e1);
- X
- X e1 = expr_alloc();
- X e1->e_op = OP_WORD;
- X e1->e_word = str_from_c("not");
- X e3 = expr_alloc();
- X e3->e_op = OP_FUNC;
- X el_append(&e3->e_list, e1);
- X el_append(&e3->e_list, e2);
- X expr_free(e1);
- X expr_free(e2);
- X
- X hash_if(e3);
- X expr_free(e3);
- X trace((/*{*/"}\n"));
- X}
- X
- X
- X/*
- X * NAME
- X * hash_elif - process #elif directive
- X *
- X * SYNOPSIS
- X * void hash_elif(expr*);
- X *
- X * DESCRIPTION
- X * The hash_elif function is used to provess #elif directives.
- X *
- X * RETURNS
- X * void
- X */
- X
- Xstatic void hash_elif _((expr *));
- X
- Xstatic void
- Xhash_elif(ep)
- X expr *ep;
- X{
- X trace(("hash_elif(ep = %08lX)\n{\n"/*}*/, ep));
- X if (!stack)
- X yyerror("#elif without matching #if");
- X else
- X {
- X switch (stack->state)
- X {
- X case 1:
- X stack->pass = 0;
- X stack->state = 1;
- X lex_passing(0);
- X break;
- X
- X case 2:
- X switch (expr_eval_condition(ep))
- X {
- X case -1:
- X yyerror("condition evaluation failed");
- X /* fall through... */
- X
- X case 0:
- X stack->pass = 0;
- X stack->state = 2;
- X lex_passing(0);
- X break;
- X
- X default:
- X stack->pass = 1;
- X stack->state = 1;
- X lex_passing(1);
- X break;
- X }
- X break;
- X
- X case 3:
- X stack->pass = 0;
- X stack->state = 3;
- X yyerror("#elif after #else");
- X lex_passing(0);
- X break;
- X }
- X }
- X trace((/*{*/"}\n"));
- X}
- X
- X
- X/*
- X * NAME
- X * hash_else - process #else directive
- X *
- X * SYNOPSIS
- X * void hash_else(void);
- X *
- X * DESCRIPTION
- X * The hash_else function is used to process #else directives.
- X *
- X * RETURNS
- X * void
- X */
- X
- Xstatic void hash_else _((void));
- X
- Xstatic void
- Xhash_else()
- X{
- X trace(("hash_else()\n{\n"/*}*/));
- X if (!stack)
- X yyerror("#else without matching #if");
- X else
- X {
- X switch (stack->state)
- X {
- X case 1:
- X stack->pass = 0;
- X stack->state = 3;
- X lex_passing(0);
- X break;
- X
- X case 2:
- X stack->pass = 1;
- X stack->state = 3;
- X lex_passing(1);
- X break;
- X
- X case 3:
- X stack->pass = 0;
- X stack->state = 3;
- X yyerror("#else after #else");
- X lex_passing(0);
- X break;
- X }
- X }
- X trace((/*{*/"}\n"));
- X}
- X
- X
- X/*
- X * NAME
- X * hash_endif - process #endif directive
- X *
- X * SYNOPSIS
- X * void hash_endif(void);
- X *
- X * DESCRIPTION
- X * The hash_endif function is used to process #endif directives.
- X *
- X * RETURNS
- X * void
- X */
- X
- Xstatic void hash_endif _((void));
- X
- Xstatic void
- Xhash_endif()
- X{
- X trace(("hash_endif()\n{\n"/*}*/));
- X if (!stack)
- X yyerror("#endif without matching #if");
- X else
- X {
- X cond *c;
- X
- X c = stack;
- X stack = c->next;
- X cond_free(c);
- X lex_passing(stack ? stack->pass : 1);
- X }
- X trace((/*{*/"}\n"));
- X}
- X
- X
- X/*
- X * NAME
- X * hash_pragma - process #pragma directive
- X *
- X * SYNOPSIS
- X * void hash_pragma(elist *elp);
- X *
- X * DESCRIPTION
- X * The hash_pragma function is used to process #pragma directives.
- X *
- X * RETURNS
- X * void
- X */
- X
- Xstatic void hash_pragma _((elist *));
- X
- Xstatic void
- Xhash_pragma(elp)
- X elist *elp;
- X{
- X static string_ty *once;
- X
- X trace(("hash_if(elp = %08lX)\n{\n"/*}*/, elp));
- X if (stack && !stack->pass)
- X goto ret;
- X
- X /*
- X * see if it was "#pragma once"
- X */
- X if (!once)
- X once = str_from_c("once");
- X if
- X (
- X elp->el_nexprs == 1
- X &&
- X elp->el_expr[0]->e_op == OP_WORD
- X &&
- X str_equal(elp->el_expr[0]->e_word, once)
- X )
- X {
- X wl_append_unique(&done_once, lex_cur_file());
- X goto ret;
- X }
- X
- X /*
- X * add more pragma's here
- X */
- X
- X ret:
- X trace((/*{*/"}\n"));
- X}
- X
- X%}
- X
- X/*
- X * this list must be IDENTICAL to the list in parse.y
- X */
- X%token CATENATE
- X%token COLON
- X%token DATA
- X%token DATAEND
- X%token ELSE
- X%token EQUALS
- X%token FAIL
- X%token IF
- X%token LBRACE
- X%token LBRAK
- X%token LOOP
- X%token LOOPSTOP
- X%token RBRACE
- X%token RBRAK
- X%token SEMICOLON
- X%token SET
- X%token THEN
- X%token UNSETENV
- X%token WORD
- X
- X/*
- X * this list must not appear in parse.y
- X */
- X%token HASH_ELIF
- X%token HASH_ELSE
- X%token HASH_ENDIF
- X%token HASH_IF
- X%token HASH_IFDEF
- X%token HASH_IFNDEF
- X%token HASH_INCLUDE
- X%token HASH_PRAGMA
- X
- X%left CATENATE
- X%right ELSE
- X
- X%union
- X{
- X expr *lv_expr;
- X elist lv_elist;
- X string_ty *lv_word;
- X}
- X
- X%type <lv_elist> elist
- X%type <lv_word> WORD
- X%type <lv_expr> expr
- X
- X%%
- X
- X/*
- X * note that the grammar accepts a single line.
- X * this means that 0 (end-of-input) must be sent on end-of-line.
- X */
- X
- Xhashline
- X : HASH_INCLUDE expr
- X {
- X hash_include($2);
- X expr_free($2);
- X }
- X | HASH_IF expr
- X {
- X hash_if($2);
- X expr_free($2);
- X }
- X | HASH_IFDEF expr
- X {
- X hash_ifdef($2);
- X expr_free($2);
- X }
- X | HASH_IFNDEF expr
- X {
- X hash_ifndef($2);
- X expr_free($2);
- X }
- X | HASH_ELIF expr
- X {
- X hash_elif($2);
- X expr_free($2);
- X }
- X | HASH_ELSE
- X {
- X hash_else();
- X }
- X | HASH_ENDIF
- X {
- X hash_endif();
- X }
- X | HASH_PRAGMA elist
- X {
- X hash_pragma(&$2);
- X el_free(&$2);
- X }
- X | error
- X ;
- X
- X/*
- X * this expression form is the same as in parse.y
- X * except that the lbrak processing is not necessary.
- X */
- X
- Xexpr
- X : WORD
- X {
- X $$ = expr_alloc();
- X $$->e_op = OP_WORD;
- X $$->e_word = $1;
- X $1 = 0;
- X }
- X | LBRAK elist RBRAK
- X {
- X $$ = expr_alloc();
- X $$->e_op = OP_FUNC;
- X $$->e_list = $2;
- X }
- X | expr CATENATE expr
- X {
- X $$ = expr_alloc();
- X $$->e_op = OP_CAT;
- X $$->e_left = $1;
- X $$->e_right = $3;
- X }
- X ;
- X
- Xelist
- X : expr
- X {
- X el_zero(&$$);
- X el_append(&$$, $1);
- X expr_free($1);
- X }
- X | elist expr
- X {
- X $$ = $1;
- X el_append(&$$, $2);
- X expr_free($2);
- X }
- X ;
- END_OF_FILE
- if test 12945 -ne `wc -c <'cook/hashline.y'`; then
- echo shar: \"'cook/hashline.y'\" unpacked with wrong size!
- fi
- # end of 'cook/hashline.y'
- fi
- echo shar: End of archive 6 \(of 11\).
- cp /dev/null ark6isdone
- MISSING=""
- for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 11 archives.
- rm -f ark[1-9]isdone ark[1-9][0-9]isdone
- else
- echo You still need to unpack the following archives:
- echo " " ${MISSING}
- fi
- ## End of shell archive.
- exit 0
-